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

