Skip to main content
doc0blade47/next.jsdocumentation home
Sign in

Build Pipeline

Last updated Oct 12, 2026100% grounded
Relevant source files

The following files were used as context for generating this wiki page:

Overview

The build pipeline orchestrates the complete production compilation process for Next.js applications, transforming source files into optimized distribution bundles through CLI Commands, Route Discovery, and Turbopack Engine. It coordinates static code verification, multi-target compilation, static analysis worker tasks, and output finalization while supporting both Webpack and Turbopack Engine backends.

Sources: packages/next/src/build/index.ts:188-196, packages/next/src/cli/next-build.ts:6-19, packages/next/src/build/turbopack-build/impl.ts:25-30

Build Pipeline Lifecycle and Orchestration

Setup Tasks and Environment Initialization

The next build command initializes execution by loading exception listeners and environment configuration files via @next/env packages/next/src/build/index.ts:17-20. SWC bindings and lockfile patch promises are initialized alongside memory management checks for available write access and directory structures packages/next/src/build/index.ts:165-166. The build process installs required native compiler bindings, establishes tracing spans, and sets up global build contexts (NextBuildContext) before configuration loading packages/next/src/build/index.ts:166-189.

Sources: packages/next/src/build/index.ts:17-20, packages/next/src/build/index.ts:165-189

Convention File Detection and Route Discovery

Root convention files such as middleware, proxy configurations, and instrumentation hooks are discovered by scanning the root directory against standard regular expressions packages/next/src/build/index.ts:1469-1494. The build pipeline checks for deprecated Route Discovery patterns and prevents conflicting concurrent declarations, throwing an error if both middleware and proxy files exist simultaneously packages/next/src/build/index.ts:1521-1530.

Warning

Detecting both middleware and proxy filename conventions simultaneously at the root level throws an immediate build error. Applications must migrate exclusively to proxy using @next/codemod packages/next/src/build/index.ts:1521-1530.

Sources: packages/next/src/build/index.ts:1469-1494, packages/next/src/build/index.ts:1521-1530

Type Checking and Static Verification

Worker Execution and Implementation Selection

TypeScript setup and validation are delegated to a dedicated worker thread via verifyAndRunTypeScript, releasing compiler memory before compilation proceeds packages/next/src/build/type-check.ts:13-21. When type checking is enabled and the TypeScript CLI flag is inactive, the build pipeline instantiates a worker running verify-typescript-setup with exposed method verifyAndRunTypeScriptInWorker packages/next/src/build/type-check.ts:45-57. If useTypeScriptCli is enabled or checking is bypassed, execution falls back to in-process invocation packages/next/src/build/type-check.ts:58-64.

typeCheckWorker = new Worker(
  require.resolve('../lib/verify-typescript-setup'),
  {
    exposedMethods: ['verifyAndRunTypeScriptInWorker'],
    debuggerPortOffset: -1,
    isolatedMemory: false,
    numWorkers: 1,
    enableWorkerThreads,
    maxRetries: 0,
  }
) as typeof typeCheckWorker

Sources: packages/next/src/build/type-check.ts:46-56

Caution

If a worker call encounters retry exhaustion (Call retries were exceeded), the build flushes telemetry and terminates with process.exit(1) rather than propagating internal Jest worker errors packages/next/src/build/type-check.ts:185-193.

Sources: packages/next/src/build/type-check.ts:185-193

Lifecycle and Telemetry Reporting

The startTypeChecking routine orchestrates the verification span (run-typescript), tracking check duration and reporting telemetry metrics upon completion packages/next/src/build/type-check.ts:96-184.

Parameter / MetricSource FieldPurpose
durationInSecondstypeCheckEnd[0]Duration of the type checking span in seconds
typescriptVersionverifyResult.versionResolved TypeScript compiler version
inputFilesCountverifyResult.result?.inputFilesCountNumber of input files processed by the checker
totalFilesCountverifyResult.result?.totalFilesCountTotal file count in the TypeScript project program
incrementalverifyResult.result?.incrementalWhether incremental compilation was utilized
typeCheckModeverifyResult.typeCheckModeActive mode utilized for type verification

Sources: packages/next/src/build/type-check.ts:173-183

Entrypoint Creation and Route Mapping

Compiler Entrypoint Generation and Mappings

The build pipeline creates compiler entrypoints by executing createEntrypoints, which processes mapped app paths, root paths, and pages to populate Webpack EntryObject tables for the client, server, and edgeServer compilers packages/next/src/build/entries.ts:388-411. Before processing individual page handlers, createEntrypoints normalizes catch-all routes and validates route matching coherence across appPathsPerRoute packages/next/src/build/entries.ts:418-435. When config.experimental.strictRouteMatching is enabled, the pipeline checks for missing canonical interception routes, incompatible parallel route slots, and unmatched app pages, throwing consolidated route-matching errors if inconsistencies are detected packages/next/src/build/entries.ts:451-526.

const missingCanonicalInterceptionRoutes = config.experimental
  .strictRouteMatching
  ? findMissingCanonicalInterceptionRoutes(appPagePathsPerRoute)
  : []
const routeMatchingErrors: Error[] = []
if (missingCanonicalInterceptionRoutes.length > 0) {
  routeMatchingErrors.push(
    new MissingCanonicalInterceptionRoutesError(
      missingCanonicalInterceptionRoutes
    )
  )
}
if (incompatibleParallelRouteSlots.length > 0) {
  routeMatchingErrors.push(
    new IncompatibleParallelRouteSlotsError(
      await Promise.all(
        incompatibleParallelRouteSlots.map(async (incompatibleRoute) => {
          const layoutPagePath = posix.join(
            incompatibleRoute.layoutPath,
            'layout'
          )
          const layoutFile = await findPageFile(
            appDir,
            layoutPagePath,
            pageExtensions,
            true
          )
 
          return {
            ...incompatibleRoute,
            layoutFile: relative(
              rootDir,
              layoutFile
                ? join(appDir, layoutFile)
                : join(appDir, layoutPagePath)
            ),
          }
        })
      )
    )
  )
}
if (unmatchedAppPages.length > 0) {
  routeMatchingErrors.push(
    new UnmatchedAppPagesError(
      unmatchedAppPages.map((appPath) => {
        const absolutePagePath = appPageFiles.get(appPath)
        if (!absolutePagePath) return appPath
 
        return relative(
          rootDir,
          getPageFilePath({
            absolutePagePath,
            pagesDir,
            appDir,
            rootDir,
          })
        )
      })
    )
  )
}
if (routeMatchingErrors.length > 0) {
  const warning = getStrictRouteMatchingDefaultWarning(config)
  if (warning) {
    Log.warnOnce(warning)
  }
}
if (routeMatchingErrors.length === 1) {
  throw routeMatchingErrors[0]
}
if (routeMatchingErrors.length > 1) {
  throw new Error(
    routeMatchingErrors.map((error) => error.message).join('\n\n')
  )
}

Sources: packages/next/src/build/entries.ts:451-526

Warning

Mismatched parallel route slots or missing canonical interception routes during strict route matching will cause the build pipeline to throw validation errors immediately before entrypoints are compiled packages/next/src/build/entries.ts:451-526.

Sources: packages/next/src/build/entries.ts:451-526

Runtime Selection and Loader Routing

The runtime target and loader configuration for a given page are determined by evaluating file types, runtimes, and page types through runDependingOnPageType and specific builder helpers packages/next/src/build/entries.ts:322-386. Depending on whether a route belongs to the Pages router, App router, API route handlers, instrumentation hooks, or middleware, the pipeline selects the appropriate compilation strategy.

Page / File TypeConditionTarget Runtimes / Handlers
Root Instrumentation HookpageType === PAGE_TYPES.ROOT && isInstrumentationHookFile(page)Server and Edge Server packages/next/src/build/entries.ts:330-337
Proxy FileisProxyFile(page)Server (Node.js) packages/next/src/build/entries.ts:339-342
Middleware FileisMiddlewareFile(page)Server (if nodejs runtime) or Edge Server packages/next/src/build/entries.ts:344-352
API RouteisAPIRoute(page)Edge Server (if Edge runtime) or Server packages/next/src/build/entries.ts:354-362
Document Pagepage === '/_document'Server (Node.js) packages/next/src/build/entries.ts:363-366
Core App Pages (_app, _error, 404, 500)page === '/_app' || page === '/_error' || page === '/404' || page === '/500'Client and Server packages/next/src/build/entries.ts:367-376
Edge Runtime PageisEdgeRuntime(pageRuntime)Client and Edge Server packages/next/src/build/entries.ts:377-381

Sources: packages/next/src/build/entries.ts:322-386

Tip

When edgeServer.instrumentation is the sole entry in the edge server object, the build pipeline removes the redundant edge instrumentation entry since instrumentation can execute purely on the Node.js server packages/next/src/build/entries.ts:798-803.

Sources: packages/next/src/build/entries.ts:798-803

Entrypoint Finalization and Webpack Layers

Once initial entry objects are assembled, finalizeEntrypoint assigns the required webpack configuration layers (layer), runtimes (runtime), and chunking parameters based on the active compilerType (server, edgeServer, or client) packages/next/src/build/entries.ts:813-915. For server-side entries, API routes are assigned WEBPACK_LAYERS.apiNode, instrumentation hooks map to WEBPACK_LAYERS.instrument, and React Server Components map to WEBPACK_LAYERS.reactServerComponents packages/next/src/build/entries.ts:835-852. Edge server entries are configured with EDGE_RUNTIME_WEBPACK runtime, assignment library structures (_ENTRIES), and disabled async chunks (asyncChunks: false) packages/next/src/build/entries.ts:853-867. Client entries configure dependency relationships (dependOn) pointing to runtime chunks such as CLIENT_STATIC_FILES_RUNTIME_MAIN or CLIENT_STATIC_FILES_RUNTIME_MAIN_APP packages/next/src/build/entries.ts:868-911.

switch (compilerType) {
  case COMPILER_NAMES.server: {
    const layer = isApi
      ? WEBPACK_LAYERS.apiNode
      : isInstrumentation
        ? WEBPACK_LAYERS.instrument
        : isServerComponent
          ? WEBPACK_LAYERS.reactServerComponents
          : name.startsWith('pages/')
            ? WEBPACK_LAYERS.pagesDirNode
            : undefined
 
    return {
      publicPath: isApi ? '' : undefined,
      runtime: isApi ? 'webpack-api-runtime' : 'webpack-runtime',
      layer,
      ...entry,
    }
  }
  case COMPILER_NAMES.edgeServer: {
    return {
      layer: isApi
        ? WEBPACK_LAYERS.apiEdge
        : isMiddlewareFilename(name) || isInstrumentation
          ? WEBPACK_LAYERS.middleware
          : name.startsWith('pages/')
            ? WEBPACK_LAYERS.pagesDirEdge
            : undefined,
      library: { name: ['_ENTRIES', `middleware_[name]`], type: 'assign' },
      runtime: EDGE_RUNTIME_WEBPACK,
      asyncChunks: false,
      ...entry,
    }
  }

Sources: packages/next/src/build/entries.ts:834-867

Webpack Production Compilation Execution

Worker-Dispatched Compilation and Sequencing

Production bundling delegates compilation tasks to isolated worker threads or executes them synchronously based on configuration flags packages/next/src/build/webpack-build/index.ts:131-163. When worker isolation is enabled via webpackBuildWithWorker, the build pipeline processes compilers sequentially in a predefined order: server, edge-server, and client packages/next/src/build/webpack-build/index.ts:14-18, packages/next/src/build/webpack-build/index.ts:34-121. Each compiler runs inside a separate worker process initialized with serialized build contexts, trace states, and telemetry instances packages/next/src/build/webpack-build/impl.ts:405-437.

Sources: packages/next/src/build/webpack-build/impl.ts:246-299, packages/next/src/build/webpack-build/index.ts:34-121

Warning

Server compilers must execute before the client compiler so that client component boundaries discovered during server rendering are injected into the client entry configuration via pluginState.injectedClientEntries packages/next/src/build/webpack-build/impl.ts:232-288.

Sources: packages/next/src/build/webpack-build/impl.ts:232-288

Compiler Execution and Lifecycle Handling

The low-level compiler runner initializes the bundler via getWebpackBundler(), preserves file system caching instances (inputFileSystem), tracks fsStartTime, and guarantees that compilers are properly closed upon completion packages/next/src/build/compiler.ts:39-92.

Function / HelperFile PathPurpose
runCompilerpackages/next/src/build/compiler.tsExecutes a Webpack configuration, manages file system bindings, and coordinates compiler closure packages/next/src/build/compiler.ts:39-92
closeCompilerpackages/next/src/build/compiler.tsInvokes Webpack 5 compiler.close() to flush persistent caches packages/next/src/build/compiler.ts:32-37
generateStatspackages/next/src/build/compiler.tsExtracts errors, warnings, and module traces from compilation stats objects packages/next/src/build/compiler.ts:11-28
webpackBuildImplpackages/next/src/build/webpack-build/impl.tsOrchestrates entry point generation, compiler runs, message formatting, and error throwing packages/next/src/build/webpack-build/impl.ts:73-402

Sources: packages/next/src/build/compiler.ts:11-92, packages/next/src/build/webpack-build/impl.ts:73-402]

Note

Webpack 5 requires compilers to be explicitly closed to persist filesystem and memory caches correctly; closeCompiler checks for the presence of the close method for backwards compatibility packages/next/src/build/compiler.ts:30-37.

Sources: packages/next/src/build/compiler.ts:30-37

Error Formatting and Fatal Exit Codes

Post-compilation messages are formatted using formatWebpackMessages, and error thresholds determine whether the build terminates or proceeds packages/next/src/build/webpack-build/impl.ts:324-401. If errors are encountered, the builder truncates output lists exceeding 5 entries to minimize noise and scans messages for specific diagnostic conditions packages/next/src/build/webpack-build/impl.ts:338-377.

if (
  error.indexOf('private-next-pages') > -1 &&
  error.indexOf('does not contain a default export') > -1
) {
  const page_name_regex = /'private-next-pages\/(?<page_name>[^']*)'/
  const parsed = page_name_regex.exec(error)
  const page_name = parsed && parsed.groups && parsed.groups.page_name
  throw new Error(
    `webpack build failed: found page without a React Component as default export in pages/${page_name}\n\nSee https://nextjs.org/docs/messages/page-without-valid-component for more info.`
  )
}
 
console.error(error)
console.error()
 
if (
  error.indexOf('private-next-pages') > -1 ||
  error.indexOf('__next_polyfill__') > -1
) {
  const err = new Error(
    'webpack config.resolve.alias was incorrectly overridden. https://nextjs.org/docs/messages/invalid-resolve-alias'
  ) as NextError
  err.code = 'INVALID_RESOLVE_ALIAS'
  throw err
}
const err = new Error(
  `Build failed because of ${process.env.NEXT_RSPACK ? 'Rspack' : 'webpack'} errors`
) as NextError
err.code = 'WEBPACK_ERRORS'
throw err

Sources: packages/next/src/build/webpack-build/impl.ts:348-377

Static Analysis and Worker Tasks

Worker Environment Initialization and Task Dispatch

Next.js offloads heavy static analysis tasks, such as determining page static rendering properties, to isolated worker threads via packages/next/src/build/worker.ts. Before executing static analysis operations, the worker entry point configures the runtime environment by establishing the global client asset suffix for Turbopack compiled code using process.env.__NEXT_PRERENDER_CLIENT_ASSET_SUFFIX.

// Set the global asset suffix for Turbopack compiled code to use during prerendering
;(globalThis as any).NEXT_CLIENT_ASSET_SUFFIX =
  process.env.__NEXT_PRERENDER_CLIENT_ASSET_SUFFIX
 

Sources: packages/next/src/build/worker.ts:9-12

Note

Native SWC bindings and code-frame support are installed explicitly inside the worker entry module rather than inside utility modules. This prevents devtools code-frame renderers from leaking into production server file traces while ensuring that errors thrown during static analysis render structured code frames.

Sources: packages/next/src/build/worker.ts:14-20

Worker-Dispatched Utility Exports

The worker thread acts as an execution boundary exposing specialized static verification and export handlers to the master build orchestrator.

Exported Handler / FunctionSource FilePurpose
isPageStaticpackages/next/src/build/worker.tsInstalls SWC bindings and code frame support asynchronously before delegating to isPageStaticImpl
getDefinedNamedExportspackages/next/src/build/worker.tsRe-exports module analysis utilities for identifying named exports from code
hasCustomGetInitialPropspackages/next/src/build/worker.tsRe-exports page component inspection helpers to detect custom data fetching methods
exportPagespackages/next/src/build/worker.tsDispatches export page worker tasks for static generation

Sources: packages/next/src/build/worker.ts:13-27

Post Build Manifest Processing

Adapter Output Finalization and Route Fields

The build pipeline finalizes production artifacts by constructing structured metadata objects for routing adapters, server runtimes, and deployment platforms. Each entry point generated during compilation maps to shared route fields that capture asset boundaries, runtime environments, and segment configurations.

interface SharedRouteFields {
  /**
   * id is the unique identifier of the output
   */
  id: string
  /**
   * filePath is the location on disk of the built entrypoint asset
   */
  filePath: string
  /**
   * pathname is the URL pathname the asset should be served at
   */
  pathname: string
 
  /**
   * sourcePage is the original source in the app or pages folder
   */
  sourcePage: string
 
  /**
   * runtime is which runtime the entrypoint is built for
   */
  runtime: 'nodejs' | 'edge'
 
  /**
   * assets are all necessary traced assets that could be
   * loaded by the output to handle a request e.g. traced
   * node_modules or necessary manifests for Next.js.
   * The key is the relative path from the repo root
   */
  assets: Record<string, string>
 
  /**
   * Hashes of the contents of each `assets` entry (not including the output path!)
   */
  assetsHashes: Record<string, string>
 
  /**
   * wasmAssets are bundled wasm files. The key is the (opaque) name of asset
   */
  wasmAssets?: Record<string, string>
 
  /**
   * edgeRuntime contains canonical entry metadata for invoking
   * this output in an edge runtime.
   */
  edgeRuntime?: {
    /**
     * modulePath is the canonical module path that registers this
     * output in the edge runtime.
     */
    modulePath: string
    /**
     * entryKey is the canonical key used for the global edge entry registry.
     */
    entryKey: string
    /**
     * handlerExport is the export name to invoke on the edge entry.
     */
    handlerExport: string
  }
 
  /**
   * config related to the route
   */
  config: {
    /**
     * maxDuration is a segment config to signal the max
     * execution duration a route should be allowed before
     * it's timed out
     */
    maxDuration?: number
    /**
     * preferredRegion is a segment config to signal deployment
     * region preferences to the provider being used
     */
    preferredRegion?: string | string[]
 
    /**
     * env is the environment variables to expose, this is only

Sources: packages/next/src/build/adapter/build-complete.ts:73-157

App paths sharing an identical URL pathname are collapsed into a single adapter output using orderAppPageKeysByEntry, which selects the canonical entry page to anchor merged asset tracking.

function orderAppPageKeysByEntry(appPageKeys: readonly string[]): string[] {
  const appPathsByPathname = new Map<string, string[]>()
 
  for (const page of appPageKeys) {
    const pathname = normalizeAppPath(page)
    const appPaths = appPathsByPathname.get(pathname)
 
    if (appPaths) {
      appPaths.push(page)
    } else {
      appPathsByPathname.set(pathname, [page])
    }
  }
 
  const orderedAppPageKeys: string[] = []
  for (const [pathname, appPaths] of appPathsByPathname) {
    const entryPage = selectAppPageEntry(pathname, appPaths)
    orderedAppPageKeys.push(entryPage)
 
    for (const appPath of appPaths) {

Sources: packages/next/src/build/adapter/build-complete.ts:212-231

Prerender groups classify their primary responses through specific classification dimensions, separating non-UI routes, page shells, and full prerender outputs.

PropertyAllowed Values / TypesMeaning
routeType'route' | 'fallback' | 'shell' | 'page'Canonical response category (non-UI route, partial fallback, page shell, or complete page)
response'empty' | 'initial' | 'complete'Completeness of the response prior to request-time work
compute'blocking' | 'resuming' | 'static'Request-time compute required to serve the completed response
htmlSizenumber (optional)Byte size of the prerendered HTML shell

Sources: packages/next/src/build/adapter/build-complete.ts:163-208

NFT File Tracing and Asset Mapping

Node File Tracing (NFT) processes emitted build artifacts to discover runtime dependencies, native bindings, and required configuration files. The build pipeline maps NFT file entries into asset records and computes content hashes for individual tracked files to ensure deployment bundles include every necessary module without extraneous files.

import { mapNftFileEntries, type NftJson } from '../nft'

Sources: packages/next/src/build/adapter/build-complete.ts:65-65

Note

Asset hash records track file contents explicitly across all traced dependencies (assetsHashes) to detect modifications between build and deployment phases without re-hashing output bundle files themselves.

Sources: packages/next/src/build/adapter/build-complete.ts:106-108

Build-Complete Hooks and Telemetry Dispatch

Upon finishing all compilation stages, route discovery, and manifest serialization, the orchestrator triggers build-complete hooks and records build statistics via telemetry events. Success and failure states emit distinct events (eventBuildCompleted and eventBuildFailed) along with performance timings, feature usage analytics, and diagnostic metadata updates.

eventBuildCompleted,
eventBuildFailed,

Sources: packages/next/src/build/index.ts:120-121

Was this page helpful?