The Build, and What It Produces
IntermediateThe build is where static and dynamic are decided, where the bundle size is fixed, and where most production surprises are visible before they ship.
Overview
A Next build does more than compile: it renders every static route to HTML, works out which routes must run per request, and prints a summary of both. That summary is the most informative artefact you get, and reading it after each build catches regressions no test does — a route that quietly became dynamic, a bundle that grew, a page count that exploded. Understanding what lands in `.next` also explains the deployment options later: what has to be uploaded, what can sit on a CDN, and what needs a running server at all.
What Happens
The phases, in order, and where each usually fails.
npm run build
// 1. Compile TypeScript and JSX -> JavaScript (SWC)
// 2. Lint and typecheck (unless disabled)
// 3. Bundle client chunks, per route
// 4. Prerender run every static page and write HTML
// 5. Optimise minify, tree-shake, generate the manifests
// Phase 4 is the one that surprises people: your page code RUNS at
// build time. So a build fails if a static page:
// queries a database that is not reachable from CI
// reads an environment variable that is not set in CI
// calls an API that is down
// "Error occurred prerendering page /problems" means your code threw
// during the build, not that the route is misconfigured.
// Skipping checks in the build is a trap worth naming:
// next.config.js
typescript: { ignoreBuildErrors: true } // do not
eslint: { ignoreDuringBuilds: true } // do not
// Both hide the failure until runtime. Run tsc and eslint as separate
// CI steps if the build is slow, but do not silence them.Reading the Summary
Four numbers worth watching between deploys.
Route (app) Size First Load JS
┌ ○ / 5.2 kB 98 kB
├ ● /learn/[topic]/[concept] 1.8 kB 95 kB
├ └ [+199 more paths]
├ ƒ /dashboard 3.1 kB 96 kB
+ First Load JS shared by all 89.1 kB
// 1. The SYMBOL — ○ static, ● SSG, ƒ dynamic. A route that moved from
// ○ to ƒ is the most common silent regression.
// 2. FIRST LOAD JS — what a visitor downloads before the page is
// interactive. Under ~100kB shared is healthy; watch the trend.
// 3. SHARED BY ALL — a jump here means something entered the common
// chunk, usually a library imported from a layout.
// 4. PATH COUNT — a generateStaticParams returning far more than
// expected lengthens builds and inflates the deployment.
// Diff it between deploys. Some teams fail CI on a size increase:
npx bundlesize
// or compare .next/analyze output.
// Local build times are misleading — CI is usually slower and colder.
// If builds hurt, look at the number of prerendered paths first.The Output Directory
What .next contains, and the two special output modes.
.next/
static/ hashed JS, CSS and media — CDN-cacheable forever
server/ server code, and the prerendered HTML/RSC payloads
cache/ the build and data caches — restore this in CI
BUILD_ID identifies the build; hashed filenames derive from it
// Cache .next/cache in CI or every build recompiles from scratch:
- uses: actions/cache@v4
with:
path: .next/cache
key: next-${{ hashFiles('package-lock.json') }}-${{ github.sha }}
// output: 'standalone' — a minimal self-contained server, for Docker
module.exports = { output: 'standalone' }
// .next/standalone contains a server.js and only the node_modules it
// actually needs. It turns a 1GB image into roughly 150MB.
// output: 'export' — a purely static site, no Node at all
module.exports = { output: 'export' }
// Gives up: server components with dynamic data, server actions,
// route handlers, ISR, middleware and next/image optimisation.
// Right for documentation; wrong for an application.
// A hashed filename is why a new deploy invalidates the client cache
// cleanly — and also why an OLD client asking for a removed chunk
// 404s, which is the post-deploy failure covered in the React track.Key Points to Remember
- 1Prerendering runs your page code at build time, so a missing env var or unreachable database fails the build
- 2Never silence TypeScript or ESLint in the build — run them as separate CI steps instead
- 3Watch the route symbols, First Load JS, the shared chunk and the prerendered path count between deploys
- 4Cache .next/cache in CI or every build recompiles from scratch
- 5output: standalone produces a small self-contained server for Docker; output: export gives up most of the framework
Interview Questions
Sign in to ask AriaWhy can a Next.js build fail with a database connection error?
What does First Load JS measure and what is a healthy value?
What do you give up with output: "export"?
Ask Aria about The Build, and What It Produces
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.