Build Output Analysis
Understanding your build output is essential for optimizing production deployments. It tells you exactly what's being generated and how.
Running a Production Build
npm run build
This generates optimized output in .next/ and shows a detailed report.
Reading the Build Output
Route (app) Size First Load JS
โ โ / 5.2 kB 89.1 kB
โ โ /about 2.1 kB 86.0 kB
โ โ /blog/[slug] 3.4 kB 87.3 kB
โ โ /blog/hello-world
โ โ /blog/nextjs-guide
โ โ [+8 more paths]
โ ฮป /dashboard 12.8 kB 96.7 kB
โ โ /pricing 1.8 kB 85.7 kB
โ ฮป /api/users 0 B 0 B
+ First Load JS shared by all 83.9 kB
โ chunks/framework-abc123.js 45.2 kB
โ chunks/main-def456.js 27.1 kB
โ other shared chunks 11.6 kB
Route Symbols
| Symbol | Meaning | Cached |
|---|---|---|
| โ | Static | At build time |
| โ | SSG (with generateStaticParams) | At build time |
| ฮป | Dynamic/Server-rendered | Per request |
| โ | Partial Prerender | Shell at build, dynamic at request |
Size Columns Explained
- Size: JavaScript specific to that route
- First Load JS: Total JS needed for that route = Shared + Size
The shared bundle loads once and is cached. Route-specific code loads per-navigation.
Identifying Issues
Large First Load JS
โ ฮป /dashboard 45.8 kB 129.7 kB โ ๏ธ
Red flag: First Load JS > 100 kB. Investigate:
- Heavy dependencies in client bundle?
- Can some components be Server Components?
- Can you lazy load parts?
Unexpected Dynamic Routes
โ ฮป /about 2.1 kB 86.0 kB โ ๏ธ
Why is /about dynamic? Check for:
cookies()orheaders()usagesearchParamsaccesscache: 'no-store'in fetchesdynamic = 'force-dynamic'export
The .next Directory
.next/
โโโ cache/ # Build and fetch cache
โโโ server/
โ โโโ app/ # Server-rendered pages
โ โโโ chunks/ # Server-side chunks
โโโ static/
โ โโโ chunks/ # Client-side chunks (what users download)
โโโ BUILD_ID # Unique build identifier
Analyzing Bundle Contents
Use the bundle analyzer:
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
module.exports = withBundleAnalyzer({})
ANALYZE=true npm run build
Pre-Generated Pages
The build output shows which paths are pre-generated:
โ โ /blog/[slug] 3.4 kB 87.3 kB
โ โ /blog/hello-world
โ โ /blog/nextjs-guide
โ โ /blog/react-tips
โ โ [+8 more paths]
These 11 pages are HTML files ready to serve instantly.
Static Export Check
For fully static deployments:
// next.config.js
module.exports = {
output: 'export',
}
Build fails if you have dynamic routes:
Error: Page "/dashboard" is using cookies(). Static generation
is not supported for pages using Request APIs.
Optimization Checklist
- All marketing pages should be โ (static)
- First Load JS under 100 kB per route
- Shared bundle under 90 kB
- Dynamic routes (ฮป) only where truly needed
- ISR pages (โ) have reasonable revalidation times
Summary
- Build output shows what each route generates
- Symbols indicate rendering strategy: โ static, โ SSG, ฮป dynamic
- First Load JS = shared bundle + route-specific code
- Investigate unexpected dynamic routes
- Use bundle analyzer for detailed JS breakdown
- Aim for static (โ) pages wherever possible
Up next ยท Lesson 25 of 26
Edge vs Node Runtime
3 min read
24 of 26 lessons done

