Home/Learn/Next.js/The Build, and What It Produces

The Build, and What It Produces

Intermediate
Production

The 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.

Your page code runs at build time
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.

Symbol, first load, shared chunk, path count
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.

standalone for Docker; cache .next/cache in CI
.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 Aria
1

Why can a Next.js build fail with a database connection error?

Medium
2

What does First Load JS measure and what is a healthy value?

Medium
3

What do you give up with output: "export"?

Hard

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.

Loading discussion…