# blade47/next.js Wiki ## Technical docs: System Overview URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/getting-started/system-overview
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/base-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts) - [packages/next/src/api/server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts) - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/server/render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx) - [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts) - [packages/next/src/api/navigation.react-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/api/navigation.react-server.ts) - [packages/next/src/client/index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/index.tsx) - [packages/next/src/api/navigation.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/api/navigation.ts) - [packages/next/src/api/router.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/api/router.ts) - [packages/next/navigation.js](https://github.com/blade47/next.js/blob/main/packages/next/navigation.js) - [packages/next/src/server/after/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/index.ts) - [packages/next/types.js](https://github.com/blade47/next.js/blob/main/packages/next/types.js) - [packages/next/src/client/next-dev.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-dev.ts) - [packages/next/app.js](https://github.com/blade47/next.js/blob/main/packages/next/app.js) - [packages/next/src/server/app-render/entry-base.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/entry-base.ts) - [packages/next/src/client/components/app-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx) - [packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-pages-router/pages/index.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-pages-router/pages/index.ts) - [packages/next/navigation.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/navigation.d.ts) - [packages/next/src/client/app-index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx) - [packages/create-next-app/templates/default/js/pages/index.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/js/pages/index.js) - [packages/next/src/server/next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts) - [packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-pages-router/pages/index.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-pages-router/pages/index.ts) - [packages/next/src/api/app.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/api/app.tsx) - [packages/next/src/cli/next-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts) - [packages/next/src/server/route-modules/pages/module.compiled.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/pages/module.compiled.d.ts) - [packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance-data.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance-data.ts) - [packages/next/src/api/link.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/api/link.ts) - [packages/next/script.js](https://github.com/blade47/next.js/blob/main/packages/next/script.js) - [packages/create-next-app/templates/app/js/app/page.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app/js/app/page.js) - [packages/next/client.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/client.d.ts)
## Overview Next.js delivers a comprehensive full-stack framework architecture designed to bridge server-side request processing, multi-paradigm rendering engines, and client-side hydration runtimes. By establishing robust abstractions across server execution contexts and browser environments, the framework solves complex web delivery challenges such as hybrid static-dynamic page generation, streaming component payloads, and seamless client-side navigation. Core design decisions integrate modular route matching, server action handlers, and diagnostic tooling into unified entrypoints that streamline application development and deployment. Sources: [packages/next/src/server/base-server.ts:98-102](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L98-L102), [packages/next/src/server/app-render/app-render.tsx:54-65](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L54-L65), [packages/next/src/client/index.tsx:190-208](https://github.com/blade47/next.js/blob/main/packages/next/src/client/index.tsx#L190-L208) ## Server Architecture and Request Handling ### Overview The server architecture rests upon a multi-layered abstraction that bridges high-level framework wrappers and environment-specific node infrastructure. Initialization operations flow through entrypoint modules that configure runtime environments, cryptographic polyfills, and module loaders before instantiating concrete request handlers. Sources: [packages/next/src/server/next.ts:10-13](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L10-L13), [packages/next/src/server/next-server.ts:1-4](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1-L4) ### Server Abstraction Layer The server architecture delegates initialization and routing behaviors through a structured wrapper pattern. The public entrypoint `NextServer` delegates execution to `NextNodeServer`, which inherits core routing and lifecycle management logic from the server base implementation in `packages/next/src/server/base-server.ts`. Sources: [packages/next/src/server/base-server.ts:69-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L69-L70), [packages/next/src/server/next.ts:183-185](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L183-L185), [packages/next/src/server/next-server.ts:69-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L69-L70) ```mermaid graph TD A[NextServer] -->|loads implementation| B[NextNodeServer] B -->|inherits core routing| C[Base Server Layer] C --> D[Route Matcher Managers] C --> E[Response Cache] ``` Sources: [packages/next/src/server/base-server.ts:69-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L69-L70), [packages/next/src/server/next.ts:183-185](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L183-L185), [packages/next/src/server/next-server.ts:69-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L69-L70) When custom servers invoke deprecated methods, `NextServer` intercepts them and logs warnings containing guidance toward modern request handler alternatives. Sources: [packages/next/src/server/next.ts:88-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L88-L114) | Deprecated Method | Migration Guidance | Sources | | :--- | :--- | :--- | | `setAssetPrefix` | Please configure `assetPrefix` in `next.config.js` instead. | [packages/next/src/server/next.ts:92-92](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L92-L92) | | `logError` | Please use application logging instead. | [packages/next/src/server/next.ts:93-93](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L93-L93) | | `revalidate` | Please use documented application revalidation APIs instead. | [packages/next/src/server/next.ts:95-95](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L95-L95) | | `render` | Please use `app.getRequestHandler()` with an adjusted parsed URL instead. | [packages/next/src/server/next.ts:96-97](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L96-L97) | | `renderError` | Please use `app.getRequestHandler()` with an adjusted parsed URL instead. | [packages/next/src/server/next.ts:100-101](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L100-L101) | | `render404` | Please use `app.getRequestHandler()` with an adjusted parsed URL instead. | [packages/next/src/server/next.ts:104-105](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L104-L105) | Sources: [packages/next/src/server/next.ts:88-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L88-L114) ### Request Routing and Lifecycle Execution Incoming HTTP traffic enters through request handlers mapped to node incoming messages and server responses. Dynamic request matching utilizes specialized providers for app pages, app routes, pages APIs, and standard pages routes, coordinating via route matcher managers. Sources: [packages/next/src/server/base-server.ts:98-102](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L98-L102), [packages/next/src/server/next-server.ts:144-149](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L144-L149) > [!WARNING] > Middleware matcher configurations are validated against active manifest definitions using cached weak maps; invalid matcher arrays trigger immediate invariant exceptions during request resolution. Sources: [packages/next/src/server/next-server.ts:151-167](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L151-L167) ## App Router Rendering Pipeline ### Overview The App Router rendering subsystem coordinates server component execution, React Server Component (RSC) Flight streaming, and staged prerendering. Rendering operations are driven by entrypoint modules and stream operations that bridge asynchronous storage layers (`workAsyncStorage` and `workUnitAsyncStorage`) with React's server rendering pipelines. Sources: [packages/next/src/server/app-render/app-render.tsx:18-28](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L18-L28), [packages/next/src/server/app-render/entry-base.ts:1-15](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/entry-base.ts#L1-L15) ### Flight Streaming and Server Components Execution The Server Components runtime environment leverages serialization and parsing utilities exported via base entrypoints. Streaming operations bridge request lifecycles with React Server Component streams, selectively configuring Node.js or Web stream operations based on `process.env.__NEXT_USE_NODE_STREAMS` and runtime capabilities. Sources: [packages/next/src/server/app-render/entry-base.ts:1-40](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/entry-base.ts#L1-L40), [packages/next/src/server/app-render/app-render.tsx:54-65](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L54-L65) ```mermaid graph TD A[Incoming RSC Request] -->|workAsyncStorage| B[App Render Subsystem] B -->|React Flight Stream| C{Runtime Environment} C -->|Edge / Web Streams| D[Web Stream Operations] C -->|Node Streams Enabled| E[Node Stream Operations] D --> F[Serialized Flight Payload] E --> F ``` Sources: [packages/next/src/server/app-render/app-render.tsx:18-28](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L18-L28), [packages/next/src/server/app-render/entry-base.ts:1-38](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/entry-base.ts#L1-L38) > [!NOTE] > `patchFetch` explicitly wires up `workAsyncStorage` and `workUnitAsyncStorage` to leverage React's experimental postponement and cache integration hooks during server component execution. Sources: [packages/next/src/server/app-render/entry-base.ts:117-124](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/entry-base.ts#L117-L124) ### Dynamic Rendering and Prerender Stream Operations The pipeline manages static generation and dynamic fallback behaviors across multiple stream continuation functions. These operations handle prerender states, prelude processing, and fallback recovery. Sources: [packages/next/src/server/app-render/app-render.tsx:42-47](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L42-L47) | Stream Operation Function | Target Prerender / Resume Behavior | Sources | | :--- | :--- | :--- | | `continueFizzStream` | Continues standard server-side HTML Fizz rendering streams | [packages/next/src/server/app-render/app-render.tsx:42-42](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L42-L42) | | `continueDynamicPrerender` | Resumes or proceeds with dynamic prerendering blocks | [packages/next/src/server/app-render/app-render.tsx:43-43](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L43-L43) | | `continueStaticPrerender` | Executes or resumes static prerendering output | [packages/next/src/server/app-render/app-render.tsx:44-44](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L44-L44) | | `continueDynamicHTMLResumeNode` | Resumes dynamic HTML rendering on Node.js streams | [packages/next/src/server/app-render/app-render.tsx:45-45](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L45-L45) | | `continueDynamicHTMLResumeWeb` | Resumes dynamic HTML rendering on Web standard streams | [packages/next/src/server/app-render/app-render.tsx:46-46](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L46-L46) | | `continueStaticFallbackPrerender` | Generates static fallback structures during prerender bailout | [packages/next/src/server/app-render/app-render.tsx:47-47](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L47-L47) | Sources: [packages/next/src/server/app-render/app-render.tsx:42-47](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L42-L47) > [!WARNING] > Accessing dynamic request parameters or headers without proper suspense boundaries or dynamic configuration triggers static generation bailouts via `StaticGenBailoutError` or dynamic tracking flags. Sources: [packages/next/src/server/app-render/app-render.tsx:141-154](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L141-L154) ## Pages Router Rendering Engine ### Overview The Pages Router rendering engine governs legacy document generation, initial props evaluation, and DOM serialization within `packages/next/src/server/render.tsx`. It manages server-side request routing state, handles previews, injects style registries like styled-jsx, and evaluates data fetching methods under tracked telemetry spans. Sources: [packages/next/src/server/render.tsx:431-893](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L431-L893) ### Execution Lifecycle and Data Fetching Call Chain The rendering engine initializes the request context and server router before invoking props loading and static generation checks. The call sequence progresses through these specific internal functions: `tryGetPreviewData()` → `ServerRouter` instantiation → `adaptForAppRouterInstance()` → `loadGetInitialProps()` → `getTracer().trace()` Sources: [packages/next/src/server/render.tsx:694-880](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L694-L880) > [!NOTE] > When `isSSG` and `!isFallback` are true, `getTracer().trace()` wraps route data evaluation under tracked spans with attributes including `'next.route'`. Sources: [packages/next/src/server/render.tsx:875-886](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L875-L886) ### Document Context and Serialization Handlers The rendering engine configures helper functions and context properties within `ctx` to execute document rendering and fallback operations. Error serialization adapts depending on whether `dev` mode is active, utilizing `errorToJSON` or returning a standard internal server error structure. Sources: [packages/next/src/server/render.tsx:431-447](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L431-L447), [packages/next/src/server/render.tsx:814-846](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L814-L846) | Helper Function or Context Property | Return Type / Behavior | Sources | | :--- | :--- | :--- | | `serializeError` | Returns `errorToJSON(err)` in development, or a 500 Internal Server Error object in production | [packages/next/src/server/render.tsx:431-447](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L431-L447) | | `getSafariCacheBusterQueryString` | In dev server mode with Safari user-agents (excluding Chrome), returns a timestamp query string `?ts=...` | [packages/next/src/server/render.tsx:449-457](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L449-L457) | | `ctx.AppTree` | Renders the page tree wrapped in `AppContainerWithIsomorphicFiberStructure` | [packages/next/src/server/render.tsx:824-830](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L824-L830) | | `ctx.defaultGetInitialProps` | Asynchronously executes `docCtx.renderPage`, flushes style registry styles, and returns `{ html, head, styles }` | [packages/next/src/server/render.tsx:831-845](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L831-L845) | Sources: [packages/next/src/server/render.tsx:431-457](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L431-L457), [packages/next/src/server/render.tsx:824-845](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L824-L845) ## Client Navigation and Hydration Runtime ### Overview The client navigation and hydration runtime manages bootstrap lifecycles, script streaming, browser history synchronization, and DOM hydration mechanisms across client entry points. It orchestrates initial payload extraction from `__NEXT_DATA__` and streams React Server Component chunks via the global `__next_f` flight buffer. Sources: [packages/next/src/client/index.tsx:1-64](https://github.com/blade47/next.js/blob/main/packages/next/src/client/index.tsx#L1-L64), [packages/next/src/client/app-index.tsx:38-110](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L38-L110) ### Flight Stream and Chunk Call Chain The client app bootstrap consumes and decodes streamed server payloads by registering a readable stream controller and parsing structured flight segments. The execution proceeds through these exact functions: `nextServerDataLoadingGlobal.forEach()` → `nextServerDataCallback()` → `nextServerDataRegisterWriter()` → `new ReadableStream()` → `ReactDOMClient` hydration Sources: [packages/next/src/client/app-index.tsx:79-188](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L79-L188) > [!NOTE] > Flight segments utilize identifier codes (`0` through `3`) to distinguish bootstrap initiation, partial response text, form state, and base64 binary chunks. Sources: [packages/next/src/client/app-index.tsx:58-110](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L58-L110) ### Flight Segment Types and Buffering Protocol The client flight protocol processes incoming array tuples assigned to `window.__next_f`. Chunk data is buffered if the stream writer is not yet registered, and later enqueued or base64-decoded. Sources: [packages/next/src/client/app-index.tsx:58-110](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L58-L110) | Flight Segment Type | Tuple Structure | Action and Handling | Sources | | :--- | :--- | :--- | :--- | | Bootstrap Initiation | `[isBootStrap: 0]` | Initializes `initialServerDataBuffer = []` | [packages/next/src/client/app-index.tsx:59-81](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L59-L81) | | Response Partial | `[isNotBootstrap: 1, responsePartial: string]` | Enqueues encoded string chunks to `initialServerDataWriter` or pushes to buffer | [packages/next/src/client/app-index.tsx:60-60](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L60-L60), [packages/next/src/client/app-index.tsx:82-90](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L82-L90) | | Form State | `[isFormState: 2, formState: any]` | Assigns form state data to `initialFormStateData` | [packages/next/src/client/app-index.tsx:61-61](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L61-L61), [packages/next/src/client/app-index.tsx:91-92](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L91-L92) | | Binary Data | `[isBinary: 3, responseBase64Partial: string]` | Decodes base64 string via `atob()` into a `Uint8Array` chunk | [packages/next/src/client/app-index.tsx:62-62](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L62-L62), [packages/next/src/client/app-index.tsx:93-109](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L93-L109) | Sources: [packages/next/src/client/app-index.tsx:58-110](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L58-L110) ### History State and Hydration Design Choices App Router navigation synchronizes state into browser history entries utilizing internal keys to distinguish handlers. Sources: [packages/next/src/client/components/app-router.tsx:71-99](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx#L71-L99) | Design Choice | Benefit | Cost | Sources | | :--- | :--- | :--- | :--- | | Shortened History Identifiers (`__NA` vs `__N`) | Enables precise runtime dispatch selection between App Router and legacy router without heavy parsing | Requires strict adherence to internal property naming conventions across navigation frames | [packages/next/src/client/components/app-router.tsx:79-86](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx#L79-L86) | | Buffer teeing for Cache Components (`__NEXT_CACHE_COMPONENTS`) | Allows truncation of inline Flight streams at static stage boundaries for caching | Introduces overhead by duplicating stream consumption channels conditionally | [packages/next/src/client/app-index.tsx:194-202](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L194-L202) | Sources: [packages/next/src/client/components/app-router.tsx:79-86](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx#L79-L86), [packages/next/src/client/app-index.tsx:194-202](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L194-L202) > [!WARNING] > Accessing or mutating history state properties without retaining `__NA` or `__PRIVATE_NEXTJS_INTERNALS_TREE` breaks App Router state restoration during back/forward browser navigation. Sources: [packages/next/src/client/components/app-router.tsx:73-86](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx#L73-L86), [packages/next/src/client/components/app-router.tsx:114-128](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx#L114-L128) ## Public Surface and Exported Submodules ### Overview Next.js exposes its core entrypoints, submodules, and client navigation utilities through well-defined API definitions and top-level CommonJS entry files. These modules bridge package-level requires with internal client components, server web exports, links, scripts, and navigation primitives. Sources: [packages/next/navigation.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/navigation.js#L1-L2), [packages/next/app.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/app.js#L1-L2), [packages/next/script.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/script.js#L1-L2), [packages/next/client.d.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/client.d.ts#L1-L2) ### Package Entrypoints and Exports The package layout delegates runtime resolution to distribution directories via CommonJS wrapper modules and TypeScript API source files. Sources: [packages/next/navigation.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/navigation.js#L1-L2), [packages/next/app.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/app.js#L1-L2), [packages/next/script.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/script.js#L1-L2) | Entrypoint File | Export Source / Target | Purpose / Module Type | Sources | | :--- | :--- | :--- | :--- | | `navigation.js` | `require('./dist/client/components/navigation')` | Root navigation module export for client components | [packages/next/navigation.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/navigation.js#L1-L2) | | `app.js` | `require('./dist/pages/_app')` | Pages router root application component export | [packages/next/app.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/app.js#L1-L2) | | `script.js` | `require('./dist/client/script')` | Script optimization component export | [packages/next/script.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/script.js#L1-L2) | Sources: [packages/next/navigation.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/navigation.js#L1-L2), [packages/next/app.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/app.js#L1-L2), [packages/next/script.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/script.js#L1-L2) ### Client and Server API Definitions API definitions bridge public TypeScript interfaces with internal runtime modules. Sources: [packages/next/client.d.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/client.d.ts#L1-L2), [packages/next/src/api/server.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts#L1-L2) | Entrypoint File | Export Source / Target | Purpose / Module Type | Sources | | :--- | :--- | :--- | :--- | | `client.d.ts` | `export * from './dist/client/index'` | TypeScript type definitions for client entrypoints | [packages/next/client.d.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/client.d.ts#L1-L2) | | `src/api/server.ts` | `export * from '../server/web/exports/index'` | Server-side web export utilities | [packages/next/src/api/server.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts#L1-L2) | | `src/api/navigation.ts` | `export * from '../client/components/navigation'` | Client navigation API exports | [packages/next/src/api/navigation.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/navigation.ts#L1-L2) | | `src/api/navigation.react-server.ts` | `export * from '../client/components/navigation.react-server'` | React server component navigation exports | [packages/next/src/api/navigation.react-server.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/navigation.react-server.ts#L1-L2) | | `src/api/link.ts` | `export { default } from '../client/link'`, `export * from '../client/link'` | Link component default and named exports | [packages/next/src/api/link.ts:1-3](https://github.com/blade47/next.js/blob/main/packages/next/src/api/link.ts#L1-L3) | Sources: [packages/next/client.d.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/client.d.ts#L1-L2), [packages/next/src/api/server.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts#L1-L2), [packages/next/src/api/navigation.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/navigation.ts#L1-L2), [packages/next/src/api/navigation.react-server.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/navigation.react-server.ts#L1-L2), [packages/next/src/api/link.ts:1-3](https://github.com/blade47/next.js/blob/main/packages/next/src/api/link.ts#L1-L3) > [!NOTE] > Server API exports re-export the entire server web surface via `export * from '../server/web/exports/index'`, providing edge runtime primitives and web APIs directly to server bundles. Sources: [packages/next/src/api/server.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts#L1-L2) ## Developer Tooling and Diagnostics Subsystem ### Overview The developer tooling and diagnostics subsystem manages the integration of development environment services, client-side hot module replacement bootstrap routines, and command-line system introspection tasks. It bridges the gap between running development client bundles and platform-level diagnostic reporting utilities. Sources: [packages/next/src/client/next-dev.ts:1-25](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-dev.ts#L1-L25), [packages/next/src/cli/next-info.ts:1-609](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L1-L609) ### Development Client Bootstrap and HMR Integration Client-side development initialization begins with `next-dev.ts`, which attaches global window bindings and wires up the Hot Module Replacement (HMR) client. Sources: [packages/next/src/client/next-dev.ts:1-25](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-dev.ts#L1-L25) ```typescript const devClient = initHMR() initialize({ devClient }) .then(({ assetPrefix }) => { return pageBootstrap(assetPrefix) }) .catch((err) => { console.error('Error was not caught', err) }) ``` Sources: [packages/next/src/client/next-dev.ts:17-24](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-dev.ts#L17-L24) The window object exposes `version`, a live-binded `router` getter, and an event `emitter`. Sources: [packages/next/src/client/next-dev.ts:8-15](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-dev.ts#L8-L15) ### Diagnostic CLI and System Inspection Tasks The `next-info` command-line utility provides standard and verbose diagnostic reporting for debugging environment and binary issues. It collects OS metadata, binary versions, configuration properties, and shared object linkage. Sources: [packages/next/src/cli/next-info.ts:14-608](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L14-L608) | Task Title | Target Platforms | Purpose / Action Executed | Sources | | :--- | :--- | :--- | :--- | | `Host system information` | default (`win32`, `linux`, `darwin`) | Collects WSL status, Docker container detection, and CI environment flags | [packages/next/src/cli/next-info.ts:276-301](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L276-L301) | | `Next.js installation` | default (`win32`, `linux`, `darwin`) | Enumerates Node version, package managers (`npm`, `yarn`, `pnpm`), relevant packages, and output config | [packages/next/src/cli/next-info.ts:302-330](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L302-L330) | | `Node.js diagnostic report` | default (`win32`, `linux`, `darwin`) | Retrieves `process.report?.getReport()` and strips sensitive header fields | [packages/next/src/cli/next-info.ts:331-365](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L331-L365) | | `next-swc installation` | default (`win32`, `linux`, `darwin`) | Verifies `loadBindings()` or inspects target triples and fallback directories | [packages/next/src/cli/next-info.ts:366-468](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L366-L468) | | `next-swc shared object dependencies` | platform-specific (`linux`, `win32`, `darwin`) | Invokes system tools (`ldd`, `dumpbin.exe`, `otool`, `dyld_info`) to check shared library resolution | [packages/next/src/cli/next-info.ts:473-530](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L473-L530) | Sources: [packages/next/src/cli/next-info.ts:270-531](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L270-L531) > [!WARNING] > The Node.js diagnostic report sanitizes sensitive data by explicitly deleting `header.cwd`, `header.commandLine`, `header.host`, `header.cpus`, and `header.networkInterfaces` prior to JSON serialization. Sources: [packages/next/src/cli/next-info.ts:346-352](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L346-L352) ## Related - [[Quick Start]] - [[Project Structure]] - [[Server Request Lifecycle]] --- ## Technical docs: Client Change Subscription Flow URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/how-it-works/client-change-subscription-flow
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/dev/hot-reloader-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts) - [packages/next/src/shared/lib/turbopack/utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/utils.ts) - [packages/next/src/shared/lib/magic-identifier.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/magic-identifier.ts)
## Overview ### Overview of Client Change Subscriptions and Error Deobfuscation The `SubscribeToClientChanges` to `RemoveFreeCallWrapper` execution flow handles real-time updates and diagnostic message processing between Turbopack and the Next.js development server. When code changes occur, client subscriptions stream compilation events and issues into Next.js, where they undergo rigorous formatting, ANSI styling, and identifier deobfuscation to present clean, readable error messages to developers. Sources: [packages/next/src/server/dev/hot-reloader-turbopack.ts:787-827](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts#L787-L827), [packages/next/src/shared/lib/turbopack/utils.ts:53-92](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/utils.ts#L53-L92), [packages/next/src/shared/lib/magic-identifier.ts:123-131](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/magic-identifier.ts#L123-L131) --- ### Step 1: subscribeToClientChanges The hot reloader maintains active subscription connections to entrypoint endpoints via `subscribeToClientChanges`. When an endpoint detects a file or module change, it yields a stream of `TurbopackResult` updates. The function iterates over these changes, invoking issue processing and message creation callbacks to broadcast HMR payloads to connected browser clients. Sources: [packages/next/src/server/dev/hot-reloader-turbopack.ts:787-827](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts#L787-L827) ### Step 2: processIssues As change results flow in, `processIssues` extracts any error, fatal, or warning issues from the result payload and stores them in the `currentEntryIssues` map under the specific entry key. It evaluates severity levels and can trigger immediate module build exceptions or log well-known errors depending on configuration flags. Sources: [packages/next/src/shared/lib/turbopack/utils.ts:53-92](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/utils.ts#L53-L92) ### Step 3: formatIssue `formatIssue` takes a raw `Issue` object and builds a human-readable diagnostic message. It handles file path normalization, source code frame integration, import traces, and maps known issues (such as missing dependencies or Sass requirements) to standard Next.js documentation URLs. Sources: [packages/next/src/shared/lib/turbopack/utils.ts:101-206](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/utils.ts#L101-L206) ### Step 4: renderStyledStringToErrorAnsi Issue titles, descriptions, and details are often represented as `StyledString` trees. `renderStyledStringToErrorAnsi` recursively traverses these structures, transforming text, strong emphasis, and code blocks into ANSI-colored strings suitable for terminal logging and dev overlay rendering, while delegating inner identifier strings for deobfuscation. Sources: [packages/next/src/shared/lib/turbopack/utils.ts:282-304](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/utils.ts#L282-L304) ### Step 5: applyDeobfuscation Nested inside the ANSI renderer, `applyDeobfuscation` passes raw string segments to `deobfuscateText` and post-processes the output by wrapping matched identifier groups in terminal magenta coloring to make compiler symbols easily identifiable. Sources: [packages/next/src/shared/lib/turbopack/utils.ts:283-288](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/utils.ts#L283-L288) ### Step 6: deobfuscateText `deobfuscateText` coordinates text cleaning by invoking `deobfuscateTextParts` to split input strings into raw segments and decoded magic identifiers, stitching the resulting parts back together into a clean, developer-friendly string. Sources: [packages/next/src/shared/lib/magic-identifier.ts:211-214](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/magic-identifier.ts#L211-L214) ### Step 7: deobfuscateTextParts `deobfuscateTextParts` scans strings for Turbopack magic identifiers and module metadata. Before matching magic identifiers, it initializes the cleaning pass by stripping away JavaScript runtime noise, such as free call wrappers, and decodes hexadecimal patterns back into readable symbols. Sources: [packages/next/src/shared/lib/magic-identifier.ts:143-203](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/magic-identifier.ts#L143-L203) ### Step 8: removeFreeCallWrapper At the foundation of the deobfuscation pipeline, `removeFreeCallWrapper` uses regular expressions to strip out JavaScript comma-operator function-calling boilerplate like `(0, __TURBOPACK__...__.method)`. This eliminates implementation clutter from stack traces and compiler outputs, leaving clean member expressions for error display. Sources: [packages/next/src/shared/lib/magic-identifier.ts:123-131](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/magic-identifier.ts#L123-L131) --- ## Execution Flow Diagram ```mermaid sequenceDiagram participant HotReloader as hot-reloader-turbopack.ts participant Utils as turbopack/utils.ts participant MagicID as magic-identifier.ts HotReloader->>HotReloader: subscribeToClientChanges() HotReloader->>Utils: processIssues(currentEntryIssues, key, change) Utils->>Utils: formatIssue(issue) Utils->>Utils: renderStyledStringToErrorAnsi(title) Utils->>Utils: applyDeobfuscation(str) Utils->>MagicID: deobfuscateText(str) MagicID->>MagicID: deobfuscateTextParts(text) MagicID->>MagicID: removeFreeCallWrapper(text) MagicID-->>Utils: Return cleaned/deobfuscated text parts Utils-->>HotReloader: Return formatted issue / message HotReloader->>HotReloader: sendHmr(key, message) ``` Sources: [packages/next/src/server/dev/hot-reloader-turbopack.ts:787-827](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts#L787-L827), [packages/next/src/shared/lib/turbopack/utils.ts:53-304](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/utils.ts#L53-L304), [packages/next/src/shared/lib/magic-identifier.ts:123-214](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/magic-identifier.ts#L123-L214) --- ## Decision Flow ```mermaid flowchart TD Sub[subscribeToClientChanges] --> Proc[processIssues] Proc --> HasIssues{Has Errors?} HasIssues -- Yes --> Format[formatIssue] HasIssues -- No --> Skip[Skip / Next Change] Format --> Render[renderStyledStringToErrorAnsi] Render --> DeobApp[applyDeobfuscation] DeobApp --> DeobText[deobfuscateText] DeobText --> DeobParts[deobfuscateTextParts] DeobParts --> RmWrapper[removeFreeCallWrapper] RmWrapper --> Send[Send HMR Message] ``` Sources: [packages/next/src/server/dev/hot-reloader-turbopack.ts:787-827](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts#L787-L827), [packages/next/src/shared/lib/turbopack/utils.ts:53-304](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/utils.ts#L53-L304), [packages/next/src/shared/lib/magic-identifier.ts:123-214](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/magic-identifier.ts#L123-L131) --- ## Key Observations - **Cross-Module Boundaries:** The execution path traverses from server-side Hot Module Replacement management (`hot-reloader-turbopack.ts`) into shared diagnostic formatting utilities (`turbopack/utils.ts`), and finally deep into string manipulation and identifier decoding logic (`magic-identifier.ts`). - **Error Resilience:** `subscribeToClientChanges` wraps asynchronous generator consumption in `try/catch` blocks. If an iteration error occurs, the subscription is deleted and optional error payloads are sent to clients. - **Noise Reduction:** The lower layers (`removeFreeCallWrapper` and `deobfuscateModuleId`) strip compiler-generated artifacts (such as `[app-rsc]`, `(ecmascript)`, and free-call wrappers) so that developers see concise, intuitive source references in their terminals and dev overlays. --- ## Technical docs: Quick Start URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/getting-started/quick-start
Relevant source files The following files were used as context for generating this wiki page: - [packages/create-next-app/index.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts) - [packages/create-next-app/templates/index.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts) - [packages/next/src/bin/next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/bin/next.ts) - [packages/create-next-app/create-app.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts) - [packages/next/taskfile.js](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js) - [packages/create-next-app/templates/default/js/pages/index.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/js/pages/index.js) - [run-evals.js](https://github.com/blade47/next.js/blob/main/run-evals.js) - [packages/create-next-app/templates/default-tw/js/pages/index.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw/js/pages/index.js) - [package.json](https://github.com/blade47/next.js/blob/main/package.json) - [packages/create-next-app/helpers/examples.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts) - [packages/next-codemod/bin/next-codemod.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/next-codemod.ts) - [packages/create-next-app/templates/app/js/app/page.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app/js/app/page.js) - [packages/create-next-app/templates/default-empty/js/pages/index.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-empty/js/pages/index.js) - [packages/create-next-app/templates/default-tw-empty/js/pages/index.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw-empty/js/pages/index.js) - [packages/create-next-app/templates/app-empty/js/app/page.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-empty/js/app/page.js) - [packages/create-next-app/templates/app-tw/js/app/page.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw/js/app/page.js) - [packages/create-next-app/templates/default/ts/pages/index.tsx](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/ts/pages/index.tsx) - [run-tests.js](https://github.com/blade47/next.js/blob/main/run-tests.js) - [packages/create-next-app/templates/app-tw-empty/js/app/page.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw-empty/js/app/page.js) - [packages/next/src/cli/next-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts) - [packages/next/package.json](https://github.com/blade47/next.js/blob/main/packages/next/package.json) - [packages/next/app.js](https://github.com/blade47/next.js/blob/main/packages/next/app.js) - [packages/create-next-app/templates/default-tw/ts/pages/index.tsx](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw/ts/pages/index.tsx) - [packages/create-next-app/package.json](https://github.com/blade47/next.js/blob/main/packages/create-next-app/package.json) - [packages/create-next-app/templates/default-empty/ts/pages/index.tsx](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-empty/ts/pages/index.tsx) - [packages/create-next-app/templates/app/ts/app/page.tsx](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app/ts/app/page.tsx) - [packages/create-next-app/templates/default-tw-empty/ts/pages/index.tsx](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw-empty/ts/pages/index.tsx) - [packages/next/src/client/next-dev.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-dev.ts) - [packages/next/src/cli/next-dev.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts)
## Overview Next.js provides a robust command-line interface and tooling ecosystem designed to streamline project initialization, template configuration, development server bootstrapping, and diagnostic inspection. This infrastructure addresses common friction points during application setup by automating directory scaffolding, fetching remote examples, validating runtime environments, and orchestrating build and test workflows across various architectural and styling preferences. Sources: [packages/create-next-app/index.ts:41-114](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L41-L114), [packages/create-next-app/create-app.ts:28-66](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L28-L66), [packages/next/src/bin/next.ts:138-155](https://github.com/blade47/next.js/blob/main/packages/next/src/bin/next.ts#L138-L155), [packages/next/src/cli/next-dev.ts:45-63](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L45-L63), [packages/next/src/cli/next-info.ts:270-329](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L270-L329) ## CLI Scaffolding Entry Points ### CLI Scaffolding Entry Points The `create-next-app` package initiates execution through a Node.js shebang header (`#!/usr/bin/env node`) located at the root of `packages/create-next-app/index.ts`. It registers process signal listeners (`SIGINT` and `SIGTERM`) executing `handleSigTerm` to immediately terminate the process upon interruption. Sources: [packages/create-next-app/index.ts:1-26](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L1-L26) ### Prompt State Management and Lifecycle User interactions via the `prompts` library are managed through `onPromptState`. If a user aborts prompt entry (`state.aborted`), the state handler explicitly restores the terminal cursor by writing escape codes (`\x1B[?25h`), prints a newline, and exits with code `1`. Sources: [packages/create-next-app/index.ts:27-39](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L27-L39) > [!WARNING] > Abruptly terminating prompts without restoring the terminal state leaves the cursor hidden. The `onPromptState` function specifically writes `\x1B[?25h` to prevent terminal lockups on cancellation. > > Sources: [packages/create-next-app/index.ts:27-39](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L27-L39) ### Commander CLI Flag Configuration The command-line parser relies on `commander` instantiated with `packageJson.name`. It accepts an optional `[directory]` positional argument and supports a comprehensive flag matrix for project customization. | Option Flag | Type / Argument | Description | Sources | | :--- | :--- | :--- | :--- | | `-v, --version` | None | Output the current version of `create-next-app`. | [packages/create-next-app/index.ts:42-46](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L42-L46) | | `-h, --help` | None | Display help message. | [packages/create-next-app/index.ts:49-49](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L49-L49) | | `--ts, --typescript` | None | Initialize as a TypeScript project. (default) | [packages/create-next-app/index.ts:50-50](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L50-L50) | | `--js, --javascript` | None | Initialize as a JavaScript project. | [packages/create-next-app/index.ts:51-51](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L51-L51) | | `--tailwind` | None | Initialize with Tailwind CSS config. (default) | [packages/create-next-app/index.ts:52-52](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L52-L52) | | `--react-compiler` | None | Initialize with React Compiler enabled. | [packages/create-next-app/index.ts:53-53](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L53-L53) | | `--eslint` | None | Initialize with ESLint config. | [packages/create-next-app/index.ts:54-54](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L54-L54) | | `--biome` | None | Initialize with Biome config. | [packages/create-next-app/index.ts:55-55](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L55-L55) | | `--app` | None | Initialize as an App Router project. | [packages/create-next-app/index.ts:56-56](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L56-L56) | | `--src-dir` | None | Initialize inside a `src/` directory. | [packages/create-next-app/index.ts:57-57](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L57-L57) | | `--rspack` | None | Enable Rspack as the bundler. | [packages/create-next-app/index.ts:58-58](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L58-L58) | | `--import-alias` | `refix/*>` | Specify import alias to use (default `@/*`). | [packages/create-next-app/index.ts:59-62](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L59-L62) | | `--api` | None | Initialize a headless API using the App Router. | [packages/create-next-app/index.ts:63-63](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L63-L63) | | `--empty` | None | Initialize an empty project. | [packages/create-next-app/index.ts:64-64](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L64-L64) | | `--use-npm` | None | Bootstrap the application using npm. | [packages/create-next-app/index.ts:66-68](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L66-L68) | | `--use-pnpm` | None | Bootstrap the application using pnpm. | [packages/create-next-app/index.ts:69-72](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L69-L72) | | `--use-yarn` | None | Bootstrap the application using Yarn. | [packages/create-next-app/index.ts:73-76](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L73-L76) | | `--use-bun` | None | Bootstrap the application using Bun. | [packages/create-next-app/index.ts:77-80](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L77-L80) | | `--reset, --reset-preferences` | None | Reset saved preferences for `create-next-app`. | [packages/create-next-app/index.ts:81-84](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L81-L84) | | `--skip-install` | None | Skip installing packages. | [packages/create-next-app/index.ts:85-88](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L85-L88) | | `--yes` | None | Use saved preferences or defaults for unprovided options. | [packages/create-next-app/index.ts:89-89](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L89-L89) | | `-e, --example` | `` | Bootstrap with an official example or public GitHub URL. | [packages/create-next-app/index.ts:90-98](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L90-L98) | | `--example-path` | `ath-to-example>` | Specify subdirectory path for complex GitHub examples. | [packages/create-next-app/index.ts:99-108](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L99-L108) | | `--agents-md` | None | Include AGENTS.md for coding agents. (default) | [packages/create-next-app/index.ts:109-112](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L109-L112) | | `--disable-git` | None | Skip initializing a git repository. | [packages/create-next-app/index.ts:113-113](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L113-L113) | Sources: [packages/create-next-app/index.ts:41-113](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L41-L113) ### Argument Parsing and Package Manager Resolution Commander action handlers parse the positional argument, screening out negated options (`--no-`) which can inadvertently pass into the name argument due to parser constraints. Package manager resolution evaluates explicit CLI options in order (`--use-npm`, `--use-pnpm`, `--use-yarn`, `--use-bun`) or falls back to `getPkgManager()`. Sources: [packages/create-next-app/index.ts:114-137](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L114-L137) | Design Choice | Benefit | Cost | Sources | | :--- | :--- | :--- | :--- | | Explicit flag overrides (`--use-npm`, etc.) | Direct control over package runner without environment detection heuristics | Verbose CLI surface area requiring maintenance for each runner | [packages/create-next-app/index.ts:66-80](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L66-L80), [packages/create-next-app/index.ts:128-136](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L128-L136) | | Negated option filtering via `!name.startsWith('--no-')` | Prevents parser misinterpretation of boolean flag toggles as project directory paths | Requires manual string inspection inside action handlers | [packages/create-next-app/index.ts:115-121](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L115-L121) | | Persistent preferences via `Conf` | Remembers user configuration defaults across executions | Requires disk I/O and state clearance handling (`--reset`) | [packages/create-next-app/index.ts:5-5](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L5-L5), [packages/create-next-app/index.ts:81-84](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L81-L84), [packages/create-next-app/index.ts:139-139](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L139-L139) | Sources: [packages/create-next-app/index.ts:5-5](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L5-L5), [packages/create-next-app/index.ts:66-84](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L66-L84), [packages/create-next-app/index.ts:115-139](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L115-L139) ## Project Generation and Example Extraction ### Overview The project generation and example extraction workflow coordinates filesystem validation, target directory creation, remote GitHub repository or example tarball streaming, and dependency installation. Driven by the `createApp` function, this subsystem takes the parsed configuration options from the CLI entry point, verifies write permissions and folder emptiness, and delegates to helper utilities to pull template or example sources. Sources: [packages/create-next-app/create-app.ts:28-66](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L28-L66) ### Application Generation Workflow The main generation entry point follows a strict call-chain execution walkthrough to validate and construct the project workspace: `createApp()` → `isWriteable()` → `mkdirSync()` → `isFolderEmpty()` → `getRepoInfo()` / `hasRepo()` / `existsInRepo()` → `downloadAndExtractRepo()` / `downloadAndExtractExample()` → `install()` 1. **Validation and Environment Checks**: `createApp` normalizes the root path using `resolve(appPath)` and checks directory writability via `isWriteable(dirname(root))` [packages/create-next-app/create-app.ts:134-136](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L134-L136). If the parent directory is not writeable, it exits with an error [packages/create-next-app/create-app.ts:136-144](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L136-L144). 2. **Directory Initialization**: Creates the target folder recursively via `mkdirSync(root, { recursive: true })` and inspects whether the folder is empty using `isFolderEmpty(root, appName)` [packages/create-next-app/create-app.ts:148-151](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L148-L151). 3. **Example and Repository Resolution**: If an `--example` flag is provided, `createApp` parses the value as a URL or a built-in example name [packages/create-next-app/create-app.ts:71-83](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L71-L83). When a GitHub URL is supplied, `getRepoInfo` extracts the username, repository name, branch, and file path [packages/create-next-app/helpers/examples.ts:23-62](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L23-L62), followed by `hasRepo()` to verify `package.json` existence via GitHub contents API HEAD requests [packages/create-next-app/create-app.ts:106-115](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L106-L115), [packages/create-next-app/helpers/examples.ts:64-74](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L64-L74). For non-URL example names, `existsInRepo()` verifies availability against `vercel/next.js` examples [packages/create-next-app/helpers/examples.ts:76-87](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L76-L87). 4. **Archive Download and Extraction**: Process changes directory to `root` via `process.chdir(root)` [packages/create-next-app/create-app.ts:160-160](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L160-L160). Depending on whether a custom repo or standard example was specified, `downloadAndExtractRepo` or `downloadAndExtractExample` fetches the tarball stream from `codeload.github.com` and pipes it through the `tar` package extractor (`x`) with up to 3 retries via `async-retry` [packages/create-next-app/create-app.ts:178-191](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L178-L191). 5. **Post-Extraction Asset Copying and Installation**: Copies missing `.gitignore` template files and `next-env.d.ts` for TypeScript projects [packages/create-next-app/create-app.ts:204-220](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L204-L220). If `skipInstall` is false and `package.json` exists, `install(packageManager, isOnline)` runs package installation [packages/create-next-app/create-app.ts:222-227](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L222-L227). Sources: [packages/create-next-app/create-app.ts:71-227](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L71-L227), [packages/create-next-app/helpers/examples.ts:23-87](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L23-L87), [packages/create-next-app/helpers/examples.ts:99-147](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L99-L147) > [!NOTE] > During tarball extraction via `downloadAndExtractRepo`, the helper dynamically determines `rootPath` from the first segment of the POSIX-converted paths inside the archive (`pathSegments[0]`). This avoids breaking the file filter if a GitHub repository has been renamed while the fetch URL was redirected. Sources: [packages/create-next-app/helpers/examples.ts:111-127](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L111-L127) ### Remote Repository and Example Helper Functions The example and repository helper module (`packages/create-next-app/helpers/examples.ts`) exports core utilities for fetching and validating remote templates over HTTP. | Function Name | Parameters | Return Type | Purpose | Sources | | :--- | :--- | :--- | :--- | :--- | | `isUrlOk` | `url: string` | `Promise` | Checks if a URL responds with HTTP status 200 via a `HEAD` request. | [packages/create-next-app/helpers/examples.ts:14-21](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L14-L21) | | `getRepoInfo` | `url: URL, examplePath?: string` | `Promise` | Parses GitHub repository path segments to extract username, repo name, branch, and file path. | [packages/create-next-app/helpers/examples.ts:23-62](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L23-L62) | | `hasRepo` | `RepoInfo` | `Promise` | Validates existence of `package.json` in a remote GitHub repository branch using the contents API. | [packages/create-next-app/helpers/examples.ts:64-74](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L64-L74) | | `existsInRepo` | `nameOrUrl: string` | `Promise` | Verifies if an example exists within `vercel/next.js` examples or via direct URL check. | [packages/create-next-app/helpers/examples.ts:76-87](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L76-L87) | | `downloadAndExtractRepo` | `root: string, RepoInfo` | `Promise` | Downloads tarball from `codeload.github.com` and extracts target subdirectory contents into `root`. | [packages/create-next-app/helpers/examples.ts:99-130](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L99-L130) | | `downloadAndExtractExample` | `root: string, name: string` | `Promise` | Downloads the official `vercel/next.js` canary tarball and extracts the specified example folder. | [packages/create-next-app/helpers/examples.ts:132-147](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L132-L147) | Sources: [packages/create-next-app/helpers/examples.ts:14-147](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L14-L147) ### Design Trade-Offs | Design Choice | Benefit | Cost | Sources | | :--- | :--- | :--- | :--- | | Streaming Tarball Extraction via `node:stream/promises` (`pipeline`) | Avoids writing large archive files to disk; processes archives on the fly | Requires careful filter mapping for Windows vs POSIX path separators | [packages/create-next-app/helpers/examples.ts:4-4](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L4-L4), [packages/create-next-app/helpers/examples.ts:89-147](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L89-L147) | | Automatic retry wrapper (`async-retry` with 3 retries) | Resiliency against intermittent network blips during remote tarball downloads | Increases total latency when downloading from dead or unresponsive endpoints | [packages/create-next-app/create-app.ts:2-2](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L2-L2), [packages/create-next-app/create-app.ts:178-181](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L178-L181), [packages/create-next-app/create-app.ts:188-190](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L188-L190) | | Dynamic `rootPath` detection from tar stream | Handles renamed GitHub repositories seamlessly without breaking extraction filters | Adds runtime introspection logic inside the tar filter callback | [packages/create-next-app/helpers/examples.ts:115-122](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L115-L122) | Sources: [packages/create-next-app/create-app.ts:2-2](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L2-L2), [packages/create-next-app/create-app.ts:178-190](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L178-L190), [packages/create-next-app/helpers/examples.ts:4-4](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L4-L4), [packages/create-next-app/helpers/examples.ts:89-147](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L89-L147) ## Starter Template Matrix and Layouts ### Overview The `create-next-app` package provides structured starter templates that span different routing architectures, languages, and styling engines. The installation routine defined in `packages/create-next-app/templates/index.ts` handles copying these templates, writing configuration overrides for bundlers like Rspack, configuring the React Compiler, rewriting TypeScript or JavaScript path aliases (`@/*`), and organizing files into an optional `src/` directory. Sources: [packages/create-next-app/templates/index.ts:48-211](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L48-L211) ### Template Layout Matrix The project templates provide permutations across the App Router and Pages Router, JavaScript and TypeScript, and Tailwind CSS variants alongside empty starter setups. | Template / Layout Identifier | Router Architecture | Primary Entry File (JS) | Primary Entry File (TS) | Styling & Font Configuration | Sources | | :--- | :--- | :--- | :--- | :--- | :--- | | `default` | Pages Router | `pages/index.js` | `pages/index.tsx` | CSS Modules (`Home.module.css`), Google Fonts (`Geist`, `Geist_Mono`) | [packages/create-next-app/templates/default/js/pages/index.js:1-88](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/js/pages/index.js#L1-L88), [packages/create-next-app/templates/default/ts/pages/index.tsx:1-89](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/ts/pages/index.tsx#L1-L89) | | `default-tw` | Pages Router | `pages/index.js` | `pages/index.tsx` | Tailwind CSS utility classes, Google Fonts (`Geist`, `Geist_Mono`) | [packages/create-next-app/templates/default-tw/js/pages/index.js:1-78](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw/js/pages/index.js#L1-L78), [packages/create-next-app/templates/default-tw/ts/pages/index.tsx:1-79](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw/ts/pages/index.tsx#L1-L79) | | `default-empty` | Pages Router | `pages/index.js` | `pages/index.tsx` | Minimal Head and basic `Hello world!` markup | [packages/create-next-app/templates/default-empty/js/pages/index.js:1-16](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-empty/js/pages/index.js#L1-L16), [packages/create-next-app/templates/default-empty/ts/pages/index.tsx:1-16](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-empty/ts/pages/index.tsx#L1-L16) | | `default-tw-empty` | Pages Router | `pages/index.js` | `pages/index.tsx` | Minimal Head with Tailwind / basic markup | [packages/create-next-app/templates/default-tw-empty/js/pages/index.js:1-16](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw-empty/js/pages/index.js#L1-L16), [packages/create-next-app/templates/default-tw-empty/ts/pages/index.tsx:1-16](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw-empty/ts/pages/index.tsx#L1-L16) | | `app` | App Router | `app/page.js` | `app/page.tsx` | CSS Modules (`page.module.css`), Next/Image assets | [packages/create-next-app/templates/app/js/app/page.js:1-66](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app/js/app/page.js#L1-L66), [packages/create-next-app/templates/app/ts/app/page.tsx:1-66](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app/ts/app/page.tsx#L1-L66) | | `app-tw` | App Router | `app/page.js` | `app/page.tsx` | Tailwind CSS utility layout, Next/Image assets | [packages/create-next-app/templates/app-tw/js/app/page.js:1-65](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw/js/app/page.js#L1-L65) | | `app-empty` | App Router | `app/page.js` | `app/page.tsx` | Minimal `Hello World!` main element | [packages/create-next-app/templates/app-empty/js/app/page.js:1-7](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-empty/js/app/page.js#L1-L7) | | `app-tw-empty` | App Router | `app/page.js` | `app/page.tsx` | Minimal `Hello world!` main element | [packages/create-next-app/templates/app-tw-empty/js/app/page.js:1-7](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw-empty/js/app/page.js#L1-L7) | Sources: [packages/create-next-app/templates/index.ts:43-110](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L43-L110), [packages/create-next-app/templates/default/js/pages/index.js:1-88](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/js/pages/index.js#L1-L88), [packages/create-next-app/templates/default-tw/js/pages/index.js:1-78](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw/js/pages/index.js#L1-L78), [packages/create-next-app/templates/app/js/app/page.js:1-66](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app/js/app/page.js#L1-L66), [packages/create-next-app/templates/default-empty/js/pages/index.js:1-16](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-empty/js/pages/index.js#L1-L16), [packages/create-next-app/templates/app-empty/js/app/page.js:1-7](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-empty/js/app/page.js#L1-L7), [packages/create-next-app/templates/app-tw/js/app/page.js:1-65](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw/js/app/page.js#L1-L65), [packages/create-next-app/templates/default/ts/pages/index.tsx:1-89](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/ts/pages/index.tsx#L1-L89), [packages/create-next-app/templates/app-tw-empty/js/app/page.js:1-7](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw-empty/js/app/page.js#L1-L7), [packages/create-next-app/templates/app/ts/app/page.tsx:1-66](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app/ts/app/page.tsx#L1-L66) ### Template Installation and Directory Structuring Walkthrough When installing a template, `installTemplate` executes an ordered sequence of file operations and configuration updates: 1. `copy()` — Copies matching glob patterns (`**`) from `packages/create-next-app/templates/[template]/[mode]` into `root`, omitting disabled config files (`!eslint.config.mjs`, `!biome.json`, `!postcss.config.mjs`) and renaming `gitignore` to `.gitignore` and `README-template.md` to `README.md`. 2. Bundler injection (`bundler === Bundler.Rspack`) — Reads `next.config.mjs` or `next.config.ts` and wraps `export default nextConfig;` with `withRspack(nextConfig)`. 3. React Compiler injection (`reactCompiler`) — Inserts `reactCompiler: true,` under `/* config options here */` in the Next config file. 4. Path alias configuration — Updates `tsconfig.json` or `jsconfig.json` compiler options to map path aliases (`@/*` or custom `importAlias`) to `./src/*` if `srcDir` is enabled. 5. `src/` migration (`srcDir`) — Creates the `src` directory via `fs.mkdir` and moves the standard directory names (`app`, `pages`, `styles` defined in `SRC_DIR_NAMES`) into `src/`, subsequently updating entry file references in `src/app/page` or `src/pages/index`. Sources: [packages/create-next-app/templates/index.ts:43-211](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L43-L211) > [!NOTE] > During template installation, `SRC_DIR_NAMES` explicitly restricts source directory relocation to `app`, `pages`, and `styles`. Any other top-level files or folders in the template remain at the project root. > > Sources: [packages/create-next-app/templates/index.ts:43-43](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L43-L43), [packages/create-next-app/templates/index.ts:179-191](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L179-L191) ### Design Trade-Offs | Design Choice | Benefit | Cost | Sources | | :--- | :--- | :--- | :--- | | Conditional exclusion globbing (`!eslint.config.mjs`, `!postcss.config.mjs`) | Prevents unselected linters or CSS tooling config files from polluting generated directories | Requires explicit negation filters for every optional configuration file in the copier call | [packages/create-next-app/templates/index.ts:72-76](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L72-L76) | | Post-copy text string replacement for Rspack and React Compiler | Avoids heavy Abstract Syntax Tree (AST) parsing dependencies when modifying configuration files | Fragile against custom formatting or non-standard exports in generated configuration files | [packages/create-next-app/templates/index.ts:97-125](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L97-L125) | | Concurrency-controlled file rewriting via `Sema(8)` for custom import aliases | Prevents EMFILE file descriptor exhaustion errors on large repository trees | Adds synchronization overhead when traversing and rewriting project files | [packages/create-next-app/templates/index.ts:142-177](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L142-L177) | Sources: [packages/create-next-app/templates/index.ts:72-76](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L72-L76), [packages/create-next-app/templates/index.ts:97-125](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L97-L125), [packages/create-next-app/templates/index.ts:142-177](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L142-L177) ## Development Server Startup Flow ### Overview The development server startup flow orchestrates the lifecycle from the initial Next.js binary invocation through environmental preflight validation, forked server child process creation, and client runtime bootstrapping. Sources: [packages/next/src/bin/next.ts:1-120](https://github.com/blade47/next.js/blob/main/packages/next/src/bin/next.ts#L1-L120), [packages/next/src/cli/next-dev.ts:204-521](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L204-L521), [packages/next/src/client/next-dev.ts:1-25](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-dev.ts#L1-L25) ### Binary Invocation and Preflight Validation Walkthrough When executing the Next.js CLI binary, execution proceeds through an explicit validation and hooking pipeline: 1. `require('../server/require-hook')` — Registers runtime module resolution hooks before importing core utilities. 2. Node Version Check — Validates `process.versions.node` against `process.env.__NEXT_REQUIRED_NODE_VERSION_RANGE` using `semver.satisfies`; exits with code `1` if unsupported. 3. Dependency Verification — Resolves `react` and `react-dom` via `require.resolve()`, emitting console warnings if missing from project dependencies. 4. `NextRootCommand.createCommand()` preAction Hook — Sets `NODE_ENV` (defaulting to `'development'` for `dev` and `'production'` otherwise), enforces standard environment checks, pins `process.env.NEXT_RUNTIME = 'nodejs'`, and checks for Apple Silicon Rosetta 2 translation mismatches. 5. `nextDev()` execution (`packages/next/src/cli/next-dev.ts`) — Parses bundler arguments via `parseBundlerArgs`, resolves the project directory via `getProjectDir()`, verifies project directory existence via `fileExists()`, and performs dependency preflight checks. Sources: [packages/next/src/bin/next.ts:3-116](https://github.com/blade47/next.js/blob/main/packages/next/src/bin/next.ts#L3-L116), [packages/next/src/cli/next-dev.ts:211-262](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L211-L262) > [!WARNING] > If a project contains both `sass` and `node-sass` installed concurrently, the preflight checker emits a warning recommending removal of `node-sass`. Additionally, if `@next/font` is detected in dependencies, a migration warning advising migration to built-in `next/font` is triggered. > > Sources: [packages/next/src/cli/next-dev.ts:231-260](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L231-L260) ### Dev Server Initialization and Child Process Forking The `nextDev` function initializes configuration options, inspect addresses, and memory thresholds before spawning the background worker process: ```typescript child = fork(startServerPath, { stdio: 'inherit', execArgv, env: { ...defaultEnv, ...(isTurbopack ? { TURBOPACK: process.env.TURBOPACK } : undefined), __NEXT_DEV_SERVER: '1', NEXT_PRIVATE_START_TIME: process.env.NEXT_PRIVATE_START_TIME, NEXT_PRIVATE_WORKER: '1', NEXT_PRIVATE_TRACE_ID: traceId, NEXT_PRIVATE_ENABLED_FEATURES: JSON.stringify(enabledFeatures), NEXT_PRIVATE_DEV_SPAN_ATTRS: JSON.stringify(devSpanAttrs), NODE_OPTIONS: formattedNodeOptions, WATCHPACK_WATCHER_LIMIT: os.platform() === 'darwin' ? '20' : undefined, }, }) ``` Sources: [packages/next/src/cli/next-dev.ts:394-427](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L394-L427) | Configuration Option | Default Source / Fallback | Purpose | Sources | | :--- | :--- | :--- | :--- | | `max-old-space-size` | 50% of total system memory (`os.totalmem()`) | Overrides Node.js heap limit unless `NEXT_DISABLE_MEM_OVERRIDE` is set | [packages/next/src/cli/next-dev.ts:355-365](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L355-L365) | | `WATCHPACK_WATCHER_LIMIT` | `'20'` on macOS (`darwin`), undefined elsewhere | Mitigates Node.js file watcher performance degradation on macOS | [packages/next/src/cli/next-dev.ts:413-414](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L413-L414) | | `__NEXT_DEV_SERVER` | `'1'` | Signals to the child process that it runs under development server mode | [packages/next/src/cli/next-dev.ts:400-400](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L400-L400) | | `NEXT_PRIVATE_WORKER` | `'1'` | Identifies the child process as an isolated worker instance | [packages/next/src/cli/next-dev.ts:402-402](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L402-L402) | Sources: [packages/next/src/cli/next-dev.ts:355-365](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L355-L365), [packages/next/src/cli/next-dev.ts:400-414](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L400-L414) ### Client Runtime Bootstrapping Once the development server compiles and serves client assets, the browser runtime bootstraps by initializing the development hot module replacement client and mounting window bindings: ```typescript import './register-deployment-id-global' import './webpack' import { initialize, version, router, emitter } from './' import initHMR from './dev/hot-middleware-client' import { pageBootstrap } from './page-bootstrap' window.next = { version, get router() { return router }, emitter, } const devClient = initHMR() initialize({ devClient }) .then(({ assetPrefix }) => { return pageBootstrap(assetPrefix) }) .catch((err) => { console.error('Error was not caught', err) }) ``` Sources: [packages/next/src/client/next-dev.ts:1-25](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-dev.ts#L1-L25) > [!NOTE] > `window.next.router` is defined using a getter property to maintain live bindings since the router instance is initialized asynchronously after client module evaluation. > > Sources: [packages/next/src/client/next-dev.ts:8-15](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-dev.ts#L8-L15) ## Environment Diagnostics and Version Inspection ### Overview Next.js provides built-in environment diagnostics and version inspection capabilities through diagnostic command-line utilities and codemod tooling. The system inspects host system details, compiler bindings, binary dependencies, and package configurations to diagnose runtime environment health. Sources: [packages/next/src/cli/next-info.ts:254-330](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L254-L330), [packages/next-codemod/bin/next-codemod.ts:1-25](https://github.com/blade47/next-codemod/bin/next-codemod.ts#L1-L25) ### Host System and Installation Diagnostics The verbose diagnostics routine evaluates platform compatibility, checking for `win32`, `linux`, or `darwin` platforms. It queries system wrappers to report Windows Subsystem for Linux (WSL) status, Docker containerization, continuous integration (CI) environment presence, binary toolchain versions, and relevant package versions. Sources: [packages/next/src/cli/next-info.ts:254-330](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L254-L330) | Diagnostic Category | Checked Metric | Source / Implementation Detail | Sources | | :--- | :--- | :--- | :--- | | Host System | WSL, Docker, CI | `isWsl`, `isDocker()`, `ciInfo.isCI` | [packages/next/src/cli/next-info.ts:282-292](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L282-L292) | | Binaries | Node, npm, Yarn, pnpm | `process.versions.node`, binary path execution | [packages/next/src/cli/next-info.ts:310-313](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L310-L313) | | Relevant Packages | next, eslint-config-next, react, react-dom, typescript | `getPackageVersion()` utility lookup | [packages/next/src/cli/next-info.ts:315-319](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L315-L319) | | Configuration | output configuration | `nextConfig.output` property | [packages/next/src/cli/next-info.ts:321-321](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L321-L321) | Sources: [packages/next/src/cli/next-info.ts:282-321](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L282-L321) > [!NOTE] > Node.js diagnostic reports are automatically retrieved via `process.report?.getReport()`. Sensitive fields including `cwd`, `commandLine`, `host`, `cpus`, and `networkInterfaces` are explicitly deleted prior to output rendering to prevent leaking host secrets. > > Sources: [packages/next/src/cli/next-info.ts:335-352](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L335-L352) ### SWC Binding Verification and Fallback Resolution Verification of `next-swc` inspects compiled native binaries by loading bindings and querying the target architecture triple. Sources: [packages/next/src/cli/next-info.ts:367-387](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L367-L387) The inspection call-chain executes as follows: `loadBindings()` → retrieves WASM binary preference from `nextConfig.experimental?.useWasmBinary` → evaluates `bindings.getTargetTriple()` → verifies successful target string return to confirm native binding integrity. Sources: [packages/next/src/cli/next-info.ts:374-386](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L374-L386) If primary loading fails, the diagnostic tool iterates through platform architecture triples using `@napi-rs/triples`, verifying optional dependencies and fallback directories: Sources: [packages/next/src/cli/next-info.ts:392-411](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L392-L411) ```typescript for (const triple of triples) { const triplePkgName = `@next/swc-${triple.platformArchABI}` if (tryResolve(triplePkgName)) { break } if (!fallbackBindingsDirectory) { continue } tryResolve(path.join(fallbackBindingsDirectory, triplePkgName)) ``` Sources: [packages/next/src/cli/next-info.ts:446-459](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L446-L459) > [!CAUTION] > If all target triples fail resolution and fallback checks, `next-swc` diagnostics report a failure state, indicating that native compilation acceleration is unavailable on the host architecture. > > Sources: [packages/next/src/cli/next-info.ts:396-401](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info. ## Related - [[System Overview]] - [[CLI Commands]] --- ## Technical docs: Project Structure URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/getting-started/project-structure
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/package.json](https://github.com/blade47/next.js/blob/main/packages/next/package.json) - [packages/next/taskfile.js](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js) - [package.json](https://github.com/blade47/next.js/blob/main/package.json) - [packages/next/types/$$compiled.internal.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/types/%24%24compiled.internal.d.ts) - [packages/next/constants.js](https://github.com/blade47/next.js/blob/main/packages/next/constants.js) - [packages/next/types.js](https://github.com/blade47/next.js/blob/main/packages/next/types.js) - [packages/next/src/bundles/webpack/packages/package.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/package.js) - [packages/next/turbo.json](https://github.com/blade47/next.js/blob/main/packages/next/turbo.json) - [packages/next/document.js](https://github.com/blade47/next.js/blob/main/packages/next/document.js) - [packages/next/app.js](https://github.com/blade47/next.js/blob/main/packages/next/app.js) - [packages/next/constants.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/constants.d.ts) - [packages/next/src/bundles/webpack/packages/sources.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/sources.js) - [packages/next/src/api/server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts) - [packages/next/script.js](https://github.com/blade47/next.js/blob/main/packages/next/script.js) - [packages/next/client.js](https://github.com/blade47/next.js/blob/main/packages/next/client.js) - [packages/next/navigation.js](https://github.com/blade47/next.js/blob/main/packages/next/navigation.js) - [packages/next/jest.js](https://github.com/blade47/next.js/blob/main/packages/next/jest.js) - [packages/next-codemod/package.json](https://github.com/blade47/next.js/blob/main/packages/next-codemod/package.json) - [packages/next/tsconfig.json](https://github.com/blade47/next.js/blob/main/packages/next/tsconfig.json) - [packages/next/src/server/after/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/index.ts) - [packages/next/index.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/index.d.ts) - [packages/next/src/next-devtools/entrypoint.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/entrypoint.ts) - [packages/next/src/api/constants.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/api/constants.ts) - [packages/next/tsconfig.build.json](https://github.com/blade47/next.js/blob/main/packages/next/tsconfig.build.json) - [packages/next/src/api/navigation.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/api/navigation.ts) - [packages/next/types.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/types.d.ts) - [pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml) - [turbo.json](https://github.com/blade47/next.js/blob/main/turbo.json)
## Overview The repository follows a structured monorepo topology orchestrated via pnpm workspaces and Turborepo task pipelines, establishing clear boundaries between core packages, applications, benchmarks, and native build crates. Sources: [package.json:4-7](https://github.com/blade47/next.js/blob/main/package.json#L4-L7), [pnpm-workspace.yaml:1-8](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L1-L8), [turbo.json:1-35](https://github.com/blade47/next.js/blob/main/turbo.json#L1-L35). Within the primary `next` package, functionality is organized into specialized internal subsystems, compiled vendor dependencies, and root-level module resolution facades that expose clean entry points for clients, servers, navigation, and testing while maintaining isolated compilation and build targets. Sources: [packages/next/package.json:5-82](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L5-L82), [packages/next/taskfile.js:1450-1568](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1450-L1568). ## Workspace and Monorepo Organization ### Overview The repository's workspace topology is rooted at the top-level manifest and governed by `pnpm-workspace.yaml`, which outlines the exact package patterns included in the build graph. The included patterns encompass `apps/*`, `packages/*`, `bench/*`, `crates/*/js`, `turbopack/crates/*/js`, `turbopack/crates/turbopack-tests/tests/execution`, and `turbopack/packages/*`. Sources: [pnpm-workspace.yaml:1-8](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L1-L8). Root package configurations also declare `workspaces` pointing to `"packages/*"`. Sources: [package.json:4-7](https://github.com/blade47/next.js/blob/main/package.json#L4-L7). Security and dependency hoisting are governed by `publicHoistPattern` containing `*eslint*`, allowed builds for `@ast-grep/cli`, and a `minimumReleaseAge` of `2880` minutes (48 hours) with specific exclusions for scopes such as `@next/*`, `@turbo/*`, `@vercel/*`, `@workflow/*`, `babel-plugin-react-compiler`, `next`, `react`, `react-dom`, `react-is`, `react-server-dom-*`, `scheduler`, and `turbo`. Sources: [pnpm-workspace.yaml:10-49](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L10-L49). ### Turborepo Task Pipelines Task orchestration is defined at the root via `turbo.json`, establishing global execution parameters, environment variables, and task dependency graphs. Global environment variables passed to tasks include `CI` and `NEXT_CI_RUNNER`, while `RUSTC_WRAPPER` and `SCCACHE_*` are passed through. Sources: [turbo.json:1-4](https://github.com/blade47/next.js/blob/main/turbo.json#L1-L4). The Turborepo configuration utilizes the terminal user interface (`"ui": "tui"`). Sources: [turbo.json:34](https://github.com/blade47/next.js/blob/main/turbo.json#L34). | Task Name | Dependencies (`dependsOn`) | Inputs | Outputs / Environment | Purpose | | :--- | :--- | :--- | :--- | :--- | | `build` | `[^build]` | — | `dist/**` | Package compilation task referencing upstream workspace dependencies. Sources: [turbo.json:5-9](https://github.com/blade47/next.js/blob/main/turbo.json#L5-L9). | | `dev` | `[^dev]` | — | `dist/**` | Development watch/serve task across workspace packages. Sources: [turbo.json:10-13](https://github.com/blade47/next.js/blob/main/turbo.json#L10-L13). | | `storybook` | — | — | — | Storybook execution task. Sources: [turbo.json:14](https://github.com/blade47/next.js/blob/main/turbo.json#L14). | | `build-storybook` | `[^build-storybook]` | — | `storybook-static/**` | Storybook compilation task. Sources: [turbo.json:15-18](https://github.com/blade47/next.js/blob/main/turbo.json#L15-L18). | | `test-storybook` | `[^test-storybook]` | — | — | Storybook testing task. Sources: [turbo.json:19-21](https://github.com/blade47/next.js/blob/main/turbo.json#L19-L21). | | `pack-for-isolated-tests` | — | `$TURBO_DEFAULT$, dist/**` | `packed.tgz` | Bundles package artifacts for isolated test runs. Sources: [turbo.json:22-25](https://github.com/blade47/next.js/blob/main/turbo.json#L22-L25). | | `typescript` | — | — | — | Type checking task. Sources: [turbo.json:26](https://github.com/blade47/next.js/blob/main/turbo.json#L26). | | `//#typescript` | — | — | — | Root-scoped type checking task. Sources: [turbo.json:27](https://github.com/blade47/next.js/blob/main/turbo.json#L27). | | `//#get-test-timings` | — | `run-tests.js` | `test-timings.json` (Env: `KV_REST_API_URL`, `KV_REST_API_TOKEN`) | Fetches and records test timing metrics. Sources: [turbo.json:28-32](https://github.com/blade47/next.js/blob/main/turbo.json#L28-L32). | Sources: [turbo.json:1-35](https://github.com/blade47/next.js/blob/main/turbo.json#L1-L35). > [!NOTE] > The sub-package `packages/next/turbo.json` extends the root workspace configuration (`"extends": ["//"]`) and overrides the `build` task to depend on both `@next/bundle-analyzer-ui#build` and `^build`, outputting to `dist/**`. Sources: [packages/next/turbo.json:1-10](https://github.com/blade47/next.js/blob/main/packages/next/turbo.json#L1-L10). ### Monorepo Scripts and Execution Workflow Root scripts defined in `package.json` invoke Turborepo tasks, Lerna commands, and script runners to manage testing, linting, and building. For instance, package cleaning is executed via `lerna clean -y && lerna run clean && lerna exec 'node ../../scripts/rm.mjs dist'`. Sources: [package.json:11](https://github.com/blade47/next.js/blob/main/package.json#L11). Production builds run `turbo run build --remote-cache-timeout 60 --summarize true`, while extended builds execute `turbo run build build-native-auto --remote-cache-timeout 60 --summarize true`. Sources: [package.json:12-13](https://github.com/blade47/next.js/blob/main/package.json#L12-L13). > [!IMPORTANT] > When executing development environments or testing suites across multiple bundlers (such as Webpack, Rspack, and Turbopack), scripts leverage `scripts/run-jest.sh` with specific flags like `--mode=dev`, `--bundler=webpack`, and `--headless`. Sources: [package.json:22-29](https://github.com/blade47/next.js/blob/main/package.json#L22-L29). Sources: [package.json:1-91](https://github.com/blade47/next.js/blob/main/package.json#L1-L91). ## Core Next Package Configuration ### Overview The core `next` package configuration governs the structure, build mechanics, and type declarations of the Next.js framework package located in `packages/next`. It integrates package manifests (`package.json`), TypeScript compiler options (`tsconfig.json`, `tsconfig.build.json`), taskrunner integration via Taskr, and an extensive set of top-level type definition entry points (`index.d.ts`, `types.d.ts`). Sources: [packages/next/package.json:1-102](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L1-L102), [packages/next/tsconfig.json:1-50](https://github.com/blade47/next.js/blob/main/packages/next/tsconfig.json#L1-L50), [packages/next/tsconfig.build.json:1-24](https://github.com/blade47/next.js/blob/main/packages/next/tsconfig.build.json#L1-L24), [packages/next/index.d.ts:1-20](https://github.com/blade47/next.js/blob/main/packages/next/index.d.ts#L1-L20). ### Package Manifest and Dependencies The `packages/next/package.json` file establishes the identity of the package as `"name": "next"` with version `"16.3.0-canary.51"`, setting its main entry point to `./dist/server/next.js`, binary executable mapping for `next` under `./dist/bin/next`, and type declarations root pointing to `index.d.ts`. Sources: [packages/next/package.json:1-10, 80-82](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L1-L10,_L80-L82). Runtime dependencies and peer dependencies control compilation requirements and external framework compatibility. | Dependency Type | Name / Identifier | Version / Range | Purpose | | :--- | :--- | :--- | :--- | | Dependency | `@next/env` | `16.3.0-canary.51` | Environment variable loading for Next.js. Sources: [packages/next/package.json:104](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L104). | | Dependency | `@swc/helpers` | `0.5.15` | SWC runtime helper functions. Sources: [packages/next/package.json:105](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L105). | | Dependency | `baseline-browser-mapping` | `^2.9.19` | Baseline browser mapping utility. Sources: [packages/next/package.json:106](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L106). | | Dependency | `caniuse-lite` | `^1.0.30001579` | Browser feature support database. Sources: [packages/next/package.json:107](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L107). | | Dependency | `postcss` | `8.5.10` | CSS transformations and styling pipelines. Sources: [packages/next/package.json:108](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L108). | | Dependency | `styled-jsx` | `5.1.6` | Component-scoped CSS styling. Sources: [packages/next/package.json:109](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L109). | | Peer Dependency | `@opentelemetry/api` | `^1.1.0` | Telemetry instrumentation tracing. Sources: [packages/next/package.json:112](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L112). | | Peer Dependency | `@playwright/test` | `^1.51.1` | End-to-end testing support. Sources: [packages/next/package.json:113](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L113). | | Peer Dependency | `babel-plugin-react-compiler` | `*` | React compiler optimization plugin. Sources: [packages/next/package.json:114](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L114). | | Peer Dependency | `react` | `^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0` | React core library peer requirement. Sources: [packages/next/package.json:115](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L115). | | Peer Dependency | `react-dom` | `^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0` | React DOM rendering peer requirement. Sources: [packages/next/package.json:116](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L116). | | Peer Dependency | `sass` | `^1.3.0` | Sass CSS preprocessor support. Sources: [packages/next/package.json:117](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L117). | Sources: [packages/next/package.json:103-118](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L103-L118). Package scripts configure local development (`dev`: `cross-env NEXT_SERVER_NO_MANGLE=1 taskr`), production compilation (`build`: `taskr release`), and type generation (`types`: `tsc --project tsconfig.build.json --declaration --emitDeclarationOnly --stripInternal --declarationDir dist`). Sources: [packages/next/package.json:83-87](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L83-L87). The `taskr` property specifies taskfile requirements including `./taskfile-webpack.js`, `./taskfile-ncc.js`, `./taskfile-swc.js`, and `./taskfile-watch.js`. Sources: [packages/next/package.json:95-101](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L95-L101). Sources: [packages/next/package.json:83-102](https://github.com/blade47/next.js/blob/main/packages/next/package.json#L83-L102). ### TypeScript Configuration and Build Options TypeScript options are structured across `tsconfig.json` extending `../../tsconfig-tsec.json` and `tsconfig.build.json` extending `tsconfig.json`. Sources: [packages/next/tsconfig.json:1-3](https://github.com/blade47/next.js/blob/main/packages/next/tsconfig.json#L1-L3), [packages/next/tsconfig.build.json:1-2](https://github.com/blade47/next.js/blob/main/packages/next/tsconfig.build.json#L1-L2). The compiler options enforce strictness and module bundling guidelines. | Compiler Option | Value | Purpose | | :--- | :--- | :--- | | `strict` | `true` | Enables all strict type-checking options. Sources: [packages/next/tsconfig.json:4](https://github.com/blade47/next.js/blob/main/tsconfig.json#L4). | | `stripInternal` | `true` | Omits declarations marked with `@internal` from emitted output. Sources: [packages/next/tsconfig.json:5](https://github.com/blade47/next.js/blob/main/tsconfig.json#L5). | | `esModuleInterop` | `true` | Enables interoperability between CommonJS and ES Modules. Sources: [packages/next/tsconfig.json:6](https://github.com/blade47/next.js/blob/main/tsconfig.json#L6). | | `verbatimModuleSyntax` | `true` | Preserves type-only imports and exports without transformation. Sources: [packages/next/tsconfig.json:7](https://github.com/blade47/next.js/blob/main/tsconfig.json#L7). | | `jsx` | `react-jsx` | Emits JSX transformed with React 17+ fragment/element factories. Sources: [packages/next/tsconfig.json:8](https://github.com/blade47/next.js/blob/main/tsconfig.json#L8). | | `module` | `ESNext` | Specifies ECMAScript module target generation. Sources: [packages/next/tsconfig.json:9](https://github.com/blade47/next.js/blob/main/tsconfig.json#L9). | | `target` | `ES2018` | Sets JavaScript language target version. Sources: [packages/next/tsconfig.json:10](https://github.com/blade47/next.js/blob/main/tsconfig.json#L10). | | `moduleResolution` | `bundler` | Resolves modules using bundler-style resolution rules. Sources: [packages/next/tsconfig.json:11](https://github.com/blade47/next.js/blob/main/tsconfig.json#L11). | | `types` | `["trusted-types", "jest", "node"]` | Specifies global type packages included during compilation. Sources: [packages/next/tsconfig.json:12](https://github.com/blade47/next.js/blob/main/tsconfig.json#L12). | Sources: [packages/next/tsconfig.json:4-12](https://github.com/blade47/next.js/blob/main/packages/next/tsconfig.json#L4-L12). The configuration maps absolute paths to prevent circular overwriting errors during `tsc` execution and enables auto-completion before builds. Mapped paths include `next/dist/client/app-find-source-map-url`, `next/dist/client/app-call-server`, `next/dist/compiled/@edge-runtime/ponyfill`, `next/dist/compiled/@vercel/og/satori`, and `next/dist/shared/lib/image-loader`. Sources: [packages/next/tsconfig.json:13-34](https://github.com/blade47/next.js/blob/main/packages/next/tsconfig.json#L13-L34). > [!WARNING] > `tsconfig.json` explicitly excludes output directories and legacy declaration files (`./dist/**/*`, `./*.d.ts`, `future/*.d.ts`, `image-types/global.d.ts`, `compat/*.d.ts`, `legacy/*.d.ts`, `types/compiled.d.ts`, `navigation-types/*.d.ts`, `navigation-types/compat/*.d.ts`, `experimental/**/*.d.ts`) to avoid input file overwrite conflicts. Sources: [packages/next/tsconfig.json:38-49](https://github.com/blade47/next.js/blob/main/tsconfig.json#L38-L49). Furthermore, `tsconfig.build.json` defines `"rootDir": "src"` and excludes test and storybook files (`./**/*.test.ts`, `./**/*.test.tsx`, `./**/*.stories.tsx`, `./**/storybook/**/*`) to prevent generating declarations for internal test code. Sources: [packages/next/tsconfig.build.json:1-24](https://github.com/blade47/next.js/blob/main/packages/next/tsconfig.build.json#L1-L24). ### Top-Level Type Declarations Top-level type definitions are orchestrated by `packages/next/index.d.ts`, which references global types, compiled types, styled-jsx types, and core subsystem declaration files before exporting module types. Sources: [packages/next/index.d.ts:1-20](https://github.com/blade47/next.js/blob/main/packages/next/index.d.ts#L1-L20). ```typescript /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// export { default } from './types' export * from './types' ``` Sources: [packages/next/index.d.ts:1-20](https://github.com/blade47/next.js/blob/main/packages/next/index.d.ts#L1-L20). The bridge file `packages/next/types.d.ts` directly exports all declarations originating from `./dist/types`, connecting the source declaration outputs to the package consumer interface. Sources: [packages/next/types.d.ts:1-3](https://github.com/blade47/next.js/blob/main/packages/next/types.d.ts#L1-L3). ## Root Module Resolution Facades ### Overview The `packages/next/` directory provides top-level proxy files that act as resolution facades, forwarding CommonJS `module.exports` and TypeScript type definitions directly to their compiled counterparts inside the `./dist/` directory. These entry points decouple public consumer imports (such as `next/navigation`, `next/client`, or `next/script`) from internal folder layouts and build artifacts. Sources: [packages/next/constants.js:1](https://github.com/blade47/next.js/blob/main/packages/next/constants.js#L1), [packages/next/navigation.js:1](https://github.com/blade47/next.js/blob/main/packages/next/navigation.js#L1). ### Facade Module Mapping Each top-level JavaScript facade delegates runtime execution to specific compilation outputs within the `dist` folder tree. Similarly, type declaration facades such as `packages/next/constants.d.ts` use ambient wildcard or direct module exports to expose types from the build output. Sources: [packages/next/constants.js:1](https://github.com/blade47/next.js/blob/main/packages/next/constants.js#L1), [packages/next/constants.d.ts:1](https://github.com/blade47/next.js/blob/main/packages/next/constants.d.ts#L1). | Facade Entry Point | Resolution Target | Exported Domain | Sources | | :--- | :--- | :--- | :--- | | `packages/next/constants.js` | `./dist/shared/lib/constants` | Shared application constants | Sources: [packages/next/constants.js:1](https://github.com/blade47/next.js/blob/main/packages/next/constants.js#L1) | | `packages/next/constants.d.ts` | `./dist/shared/lib/constants` | Shared constant type definitions | Sources: [packages/next/constants.d.ts:1](https://github.com/blade47/next.js/blob/main/packages/next/constants.d.ts#L1) | | `packages/next/document.js` | `./dist/pages/_document` | Pages router document template | Sources: [packages/next/document.js:1](https://github.com/blade47/next.js/blob/main/packages/next/document.js#L1) | | `packages/next/app.js` | `./dist/pages/_app` | Pages router application root | Sources: [packages/next/app.js:1](https://github.com/blade47/next.js/blob/main/packages/next/app.js#L1) | | `packages/next/script.js` | `./dist/client/script` | Client-side script optimization component | Sources: [packages/next/script.js:1](https://github.com/blade47/next.js/blob/main/packages/next/script.js#L1) | | `packages/next/client.js` | `./dist/client/index` | Client-side runtime entry | Sources: [packages/next/client.js:1](https://github.com/blade47/next.js/blob/main/packages/next/client.js#L1) | | `packages/next/navigation.js` | `./dist/client/components/navigation` | App router navigation hooks | Sources: [packages/next/navigation.js:1](https://github.com/blade47/next.js/blob/main/packages/next/navigation.js#L1) | | `packages/next/jest.js` | `./dist/build/jest/jest` | Jest testing integration presets | Sources: [packages/next/jest.js:1](https://github.com/blade47/next.js/blob/main/packages/next/jest.js#L1) | Sources: [packages/next/constants.js:1](https://github.com/blade47/next.js/blob/main/packages/next/constants.js#L1), [packages/next/constants.d.ts:1](https://github.com/blade47/next.js/blob/main/packages/next/constants.d.ts#L1), [packages/next/document.js:1](https://github.com/blade47/next.js/blob/main/packages/next/document.js#L1), [packages/next/app.js:1](https://github.com/blade47/next.js/blob/main/packages/next/app.js#L1), [packages/next/script.js:1](https://github.com/blade47/next.js/blob/main/packages/next/script.js#L1), [packages/next/client.js:1](https://github.com/blade47/next.js/blob/main/packages/next/client.js#L1), [packages/next/navigation.js:1](https://github.com/blade47/next.js/blob/main/packages/next/navigation.js#L1), [packages/next/jest.js:1](https://github.com/blade47/next.js/blob/main/packages/next/jest.js#L1). ### Runtime Facade Implementation The implementation across all runtime facades relies on synchronous CommonJS `require` calls pointing to compiled distribution targets. For instance, navigation features load directly from the client components bundle, while document and app templates load from the pages build output. Sources: [packages/next/app.js:1](https://github.com/blade47/next.js/blob/main/packages/next/app.js#L1), [packages/next/navigation.js:1](https://github.com/blade47/next.js/blob/main/packages/next/navigation.js#L1). ```javascript module.exports = require('./dist/pages/_app') ``` Sources: [packages/next/app.js:1](https://github.com/blade47/next.js/blob/main/packages/next/app.js#L1) ```javascript module.exports = require('./dist/client/components/navigation') ``` Sources: [packages/next/navigation.js:1](https://github.com/blade47/next.js/blob/main/packages/next/navigation.js#L1) ```javascript module.exports = require('./dist/build/jest/jest') ``` Sources: [packages/next/jest.js:1](https://github.com/blade47/next.js/blob/main/packages/next/jest.js#L1) > [!NOTE] > Resolution facades like `packages/next/navigation.js` and `packages/next/jest.js` decouple consumers from internal build directory structures, allowing the compiler output organization under `./dist/` to change without breaking external import paths like `next/navigation` or `next/jest`. Sources: [packages/next/navigation.js:1](https://github.com/blade47/next.js/blob/main/packages/next/navigation.js#L1), [packages/next/jest.js:1](https://github.com/blade47/next.js/blob/main/packages/next/jest.js#L1). ## Internal Subsystems and API Architecture ### Overview The codebase organizes its internal API architecture and subsystems under the `packages/next/src/` directory, exposing structured re-exports across server handlers, asynchronous lifecycle hooks, navigation primitives, shared constants, and development tools. These internal modules bridge package-level entry points to specific internal compilation domains. Sources: [packages/next/src/api/server.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts#L1-L2), [packages/next/src/api/constants.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/constants.ts#L1-L2), [packages/next/src/api/navigation.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/navigation.ts#L1-L2), [packages/next/src/server/after/index.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/index.ts#L1-L2), [packages/next/src/next-devtools/entrypoint.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/entrypoint.ts#L1-L2). ### Internal Subsystem Modules The API architecture maps internal directory modules directly to domain-specific source trees via wildcard re-exports. Server-side web APIs delegate through `packages/next/src/api/server.ts` to web export handlers, while navigation functions route through `packages/next/src/api/navigation.ts` to client navigation components. Sources: [packages/next/src/api/server.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts#L1-L2), [packages/next/src/api/navigation.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/navigation.ts#L1-L2). | Internal API Module | Delegation Target | Subsystem Domain | Sources | | :--- | :--- | :--- | :--- | | `packages/next/src/api/server.ts` | `../server/web/exports/index` | Server web runtime exports | Sources: [packages/next/src/api/server.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts#L1-L2) | | `packages/next/src/api/constants.ts` | `../shared/lib/constants` | Shared application constants | Sources: [packages/next/src/api/constants.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/constants.ts#L1-L2) | | `packages/next/src/api/navigation.ts` | `../client/components/navigation` | Client navigation components | Sources: [packages/next/src/api/navigation.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/navigation.ts#L1-L2) | | `packages/next/src/server/after/index.ts` | `./after` | Asynchronous task deferral handlers | Sources: [packages/next/src/server/after/index.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/index.ts#L1-L2) | | `packages/next/src/next-devtools/entrypoint.ts` | `./dev-overlay.browser` | Browser development overlay | Sources: [packages/next/src/next-devtools/entrypoint.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/entrypoint.ts#L1-L2) | Sources: [packages/next/src/api/server.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts#L1-L2), [packages/next/src/api/constants.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/constants.ts#L1-L2), [packages/next/src/api/navigation.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/navigation.ts#L1-L2), [packages/next/src/server/after/index.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/index.ts#L1-L2), [packages/next/src/next-devtools/entrypoint.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/entrypoint.ts#L1-L2). ### Facade Delegation Syntax Internal subsystem entry points utilize wildcard export declarations (`export * from ...`) to aggregate and expose subsystem implementations without hardcoding individual function signatures. Sources: [packages/next/src/api/server.ts:1](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts#L1), [packages/next/src/server/after/index.ts:1](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/index.ts#L1), [packages/next/src/next-devtools/entrypoint.ts:1](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/entrypoint.ts#L1). ```typescript export * from '../server/web/exports/index' ``` Sources: [packages/next/src/api/server.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts#L1-L2) ```typescript export * from './after' ``` Sources: [packages/next/src/server/after/index.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/index.ts#L1-L2) ```typescript export * from './dev-overlay.browser' ``` Sources: [packages/next/src/next-devtools/entrypoint.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/entrypoint.ts#L1-L2) > [!NOTE] > Devtools entrypoints bind directly to browser-specific overlay targets (`./dev-overlay.browser`), isolating development UI elements from production server bundles. Sources: [packages/next/src/next-devtools/entrypoint.ts:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/entrypoint.ts#L1-L2). ## Compilation Targets and Bundled Dependencies ### Overview Next.js manages third-party dependencies and compilation outputs through automated Taskfile build routines and dedicated internal bundle shims. Vendor packages such as PostCSS utilities, React runtimes, and webpack sources are compiled, bundled, or aliased into `src/compiled/` to isolate them from external module resolution conflicts and Haste module map warnings. Sources: [packages/next/taskfile.js:1431-1446](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1431-L1446), [packages/next/taskfile.js:1450-1477](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1450-L1477). ### Taskfile Build and NCC Compilation Targets The Taskfile build pipeline invokes `ncc` compilation routines to bundle external libraries into internal distribution targets. For instance, `ncc_icss_utils` resolves `icss-utils`, marks `postcss/lib/parser` as external, and outputs the bundled result directly to `src/compiled/icss-utils`. Sources: [packages/next/taskfile.js:1435-1446](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1435-L1446). ```javascript export async function ncc_icss_utils(task, opts) { await task .source(relative(__dirname, require.resolve('icss-utils'))) .ncc({ packageName: 'icss-utils', externals: { 'postcss/lib/parser': 'postcss/lib/parser', ...externals, }, }) .target('src/compiled/icss-utils') } ``` Sources: [packages/next/taskfile.js:1435-1446](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1435-L1446) ### Vendor React and Scheduler Copy Execution Walkthrough The `copy_vendor_react` task processes experimental and standard React channels through a multi-step transformation sequence. 1. **Channel and Suffix Resolution:** `copy_vendor_react_impl` inspects `opts.experimental` to determine the channel (`experimental-builtin` or `builtin`) and package suffix (`-experimental` or ``). Sources: [packages/next/taskfile.js:1451-1453](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1451-L1453). 2. **Package Name Rewriting:** `overridePackageName` parses `package.json` files and appends the channel suffix to `json.name` if absent, ensuring Haste module map warnings are avoided. Sources: [packages/next/taskfile.js:1458-1464](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1458-L1464). 3. **Module Aliasing:** `aliasVendoredReactPackages` replaces string occurrences of `require("react")`, `require("react-dom")`, and `require("scheduler")` inside CommonJS chunks with their `next/dist/compiled/` equivalents containing the appropriate package suffix. Sources: [packages/next/taskfile.js:1479-1493](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1479-L1493). 4. **AST Parsing and Modification:** `parseFile` utilizes `recast` with an Acorn parser configured for `latest` ECMAScript versions and script source types, enabling AST traversal and identifier replacement via `replaceIdentifiersInAst`. Sources: [packages/next/taskfile.js:1570-1605](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1570-L1605). Sources: [packages/next/taskfile.js:1450-1605](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1450-L1605). > [!WARNING] > React DOM CJS files undergo package aliasing but must preserve server-rendering stub mappings, preventing blanket replacement of `react-dom` references with internal compiled paths in contexts where stubs are active. Sources: [packages/next/taskfile.js:1556-1568](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1556-L1568). ### Internal Bundle Shims and Type Declarations Internal module resolution relies on type declaration shims located in `packages/next/types/$$compiled.internal.d.ts` alongside webpack facade wrappers. These files map `next/dist/compiled/*` module paths directly to upstream dependency exports or custom wrapper modules. Sources: [packages/next/types/$$compiled.internal.d.ts:487-502](https://github.com/blade47/next.js/blob/main/packages/next/types/%24%24compiled.internal.d.ts#L487-L502), [packages/next/src/bundles/webpack/packages/package.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/package.js#L1-L2). | Module Shim / Facade Path | Target Resolution | Purpose | Sources | | :--- | :--- | :--- | :--- | | `next/dist/compiled/commander` | `commander` | CLI argument parser module shim | Sources: [packages/next/types/$$compiled.internal.d.ts:487-489](https://github.com/blade47/next.js/blob/main/packages/next/types/%24%24compiled.internal.d.ts#L487-L489) | | `next/dist/compiled/jest-worker` | `jest-worker` | Worker process farm module shim | Sources: [packages/next/types/$$compiled.internal.d.ts:499-501](https://github.com/blade47/next.js/blob/main/packages/next/types/%24%24compiled.internal.d.ts#L499-L501) | | `packages/next/src/bundles/webpack/packages/package.js` | `./webpack.js`.package | Webpack package bundle entry facade | Sources: [packages/next/src/bundles/webpack/packages/package.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/package.js#L1-L2) | | `packages/next/src/bundles/webpack/packages/sources.js` | `./webpack.js`.sources | Webpack sources bundle entry facade | Sources: [packages/next/src/bundles/webpack/packages/sources.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/sources.js#L1-L2) | Sources: [packages/next/types/$$compiled.internal.d.ts:487-502](https://github.com/blade47/next.js/blob/main/packages/next/types/%24%24compiled.internal.d.ts#L487-L502), [packages/next/src/bundles/webpack/packages/package.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/package.js#L1-L2), [packages/next/src/bundles/webpack/packages/sources.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/sources.js#L1-L2). > [!TIP] > Webpack internal bundling delegates specialized exports like `package` and `sources` through local proxy files into a central `webpack.js` module core, maintaining a clean separation of bundle entry points. Sources: [packages/next/src/bundles/webpack/packages/package.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/package.js#L1-L2), [packages/next/src/bundles/webpack/packages/sources.js:1-2](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/sources.js#L1-L2). ## Related - [[System Overview]] - [[Monorepo Workspace]] --- ## Technical docs: Monorepo Workspace URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/getting-started/monorepo-workspace
Relevant source files The following files were used as context for generating this wiki page: - [packages/next-codemod/lib/agents-md.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/lib/agents-md.ts) - [packages/next-codemod/bin/__testfixtures__/next-14-installed/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/next-14-installed/pnpm-workspace.yaml) - [packages/next-codemod/bin/__testfixtures__/geo-ip-usage/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/geo-ip-usage/pnpm-workspace.yaml) - [packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-pages-router/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-pages-router/pnpm-workspace.yaml) - [package.json](https://github.com/blade47/next.js/blob/main/package.json) - [packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-pages-router/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-pages-router/pnpm-workspace.yaml) - [pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml) - [packages/next/src/cli/next-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts) - [packages/next/src/cli/internal/static-routes-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts) - [packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-app-router/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-app-router/pnpm-workspace.yaml) - [packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-app-router/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-app-router/pnpm-workspace.yaml) - [packages/next-codemod/bin/__testfixtures__/react-18-installed-mixed-router/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-18-installed-mixed-router/pnpm-workspace.yaml) - [lerna.json](https://github.com/blade47/next.js/blob/main/lerna.json) - [packages/next-codemod/bin/__testfixtures__/react-19-installed-mixed-router/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-19-installed-mixed-router/pnpm-workspace.yaml) - [conductor.json](https://github.com/blade47/next.js/blob/main/conductor.json) - [packages/next/taskfile.js](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js) - [packages/next-codemod/bin/__testfixtures__/suggest-turbopack/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/suggest-turbopack/pnpm-workspace.yaml) - [socket.yaml](https://github.com/blade47/next.js/blob/main/socket.yaml) - [packages/next-codemod/bin/__testfixtures__/no-geo-ip-usage/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/no-geo-ip-usage/pnpm-workspace.yaml) - [packages/next-codemod/bin/__testfixtures__/change-turbo-to-turbopack/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/change-turbo-to-turbopack/pnpm-workspace.yaml) - [packages/next-codemod/bin/__testfixtures__/peer-dep-out-of-range/met-range/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/peer-dep-out-of-range/met-range/pnpm-workspace.yaml) - [packages/create-next-app/templates/index.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts) - [packages/next-codemod/bin/__testfixtures__/peer-dep-out-of-range/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/peer-dep-out-of-range/pnpm-workspace.yaml) - [packages/next/src/lib/find-root.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts) - [packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml) - [packages/next/src/server/lib/chrome-devtools-workspace.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/chrome-devtools-workspace.ts) - [apps/bundle-analyzer/lib/analyze-data.ts](https://github.com/blade47/next.js/blob/main/apps/bundle-analyzer/lib/analyze-data.ts) - [packages/next-codemod/lib/handle-package.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/lib/handle-package.ts) - [turbopack/packages/node-module-trace/package.json](https://github.com/blade47/next.js/blob/main/turbopack/packages/node-module-trace/package.json) - [packages/next/src/bundles/webpack/packages/package.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/package.js)
## Overview The Monorepo Workspace provides the foundational architecture for developing, testing, and building Next.js and its associated tooling across multiple integrated packages and standalone applications. It establishes strict workspace configurations, automated build pipelines, and runtime discovery utilities to seamlessly coordinate dependencies across diverse package managers and orchestrators. Sources: [conductor.json:1-4](https://github.com/blade47/next.js/blob/main/conductor.json#L1-L4), [pnpm-workspace.yaml:1-9](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L1-L9) By enforcing structured package boundaries, deterministic lockfile resolution, and comprehensive diagnostic inspection, the workspace architecture solves complex dependency management and bundling challenges inherent in large-scale JavaScript and Rust-based hybrid repositories. Sources: [packages/next/src/lib/find-root.ts:34-65](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts#L34-L65), [packages/next/src/cli/internal/static-routes-info.ts:1-16](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L1-L16) ## Monorepo Layout and Workspace Configuration ### Workspace Management Architecture The root workspace architecture organizes the repository by integrating `pnpm`, `Lerna`, and `Conductor` orchestrations to govern dependency layout, package publishing, and developer environment management. These orchestration layers define precise package globs, publish workflows, environment defaults, and security constraints across the repository. Sources: [pnpm-workspace.yaml:1-9](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L1-L9), [lerna.json:1-19](https://github.com/blade47/next.js/blob/main/lerna.json#L1-L19), [conductor.json:1-14](https://github.com/blade47/next.js/blob/main/conductor.json#L1-L14) ### PNPM Workspace Layout The `pnpm-workspace.yaml` configuration dictates how workspaces are scanned and resolved across applications, packages, benchmarks, and Turbopack Rust-to-JS bindings. Sources: [pnpm-workspace.yaml:1-8](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L1-L8) | Package Glob Pattern | Target Component / Subsystem | Sources | | :--- | :--- | :--- | | `apps/*` | Standalone applications and consumer examples | [pnpm-workspace.yaml:2-2](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L2-L2) | | `packages/*` | Core framework packages and shared libraries | [pnpm-workspace.yaml:3-3](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L3-L3) | | `bench/*` | Performance benchmark suites | [pnpm-workspace.yaml:4-4](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L4-L4) | | `crates/*/js` | Rust crate JavaScript bindings | [pnpm-workspace.yaml:5-5](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L5-L5) | | `turbopack/crates/*/js` | Turbopack Rust crate JavaScript bindings | [pnpm-workspace.yaml:6-6](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L6-L6) | | `turbopack/crates/turbopack-tests/tests/execution` | Turbopack execution test suites | [pnpm-workspace.yaml:7-7](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L7-L7) | | `turbopack/packages/*` | Turbopack package ecosystem | [pnpm-workspace.yaml:8-8](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L8-L8) | Sources: [pnpm-workspace.yaml:1-8](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L1-L8) > [!IMPORTANT] > The pnpm workspace disables update notifications via `updateNotifier: false` and hoists specific eslint dependencies using `publicHoistPattern: ['*eslint*']` while enforcing security boundaries with `blockExoticSubdeps: true` and a 48-hour minimum release age (`minimumReleaseAge: 2880`). Sources: [pnpm-workspace.yaml:9-11](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L9-L11), [pnpm-workspace.yaml:34-35](https://github.com/blade47/next.js/blob/main/pnpm-workspace.yaml#L34-L35) ### Lerna Versioning and Publishing Lerna manages multi-package version coordination and publication pipelines at the repository root. It relies on `pnpm` as the underlying `npmClient` and restricts publishing actions to the `canary` branch targeting the public npm registry. Sources: [lerna.json:1-17](https://github.com/blade47/next.js/blob/main/lerna.json#L1-L17) | Lerna Configuration Property | Value / Scope | Sources | | :--- | :--- | :--- | | `npmClient` | `pnpm` | [lerna.json:2-2](https://github.com/blade47/next.js/blob/main/lerna.json#L2-L2) | | `packages` | `packages/*` | [lerna.json:3-5](https://github.com/blade47/next.js/blob/main/lerna.json#L3-L5) | | `command.version.exact` | `true` | [lerna.json:7-9](https://github.com/blade47/next.js/blob/main/lerna.json#L7-L9) | | `command.publish.npmClient` | `npm` | [lerna.json:10-11](https://github.com/blade47/next.js/blob/main/lerna.json#L10-L11) | | `command.publish.allowBranch` | `["canary"]` | [lerna.json:12-14](https://github.com/blade47/next.js/blob/main/lerna.json#L12-L14) | | `command.publish.registry` | `https://registry.npmjs.org/` | [lerna.json:15-15](https://github.com/blade47/next.js/blob/main/lerna.json#L15-L15) | | `version` | `16.3.0-canary.51` | [lerna.json:18-18](https://github.com/blade47/next.js/blob/main/lerna.json#L18-L18) | Sources: [lerna.json:1-18](https://github.com/blade47/next.js/blob/main/lerna.json#L1-L18) ### Conductor Orchestration and Environment The Conductor configuration establishes workspace environment defaults, lifecycle scripts, worktree branches, and developer operational recommendations. Sources: [conductor.json:1-23](https://github.com/blade47/next.js/blob/main/conductor.json#L1-L23) > [!CAUTION] > Never execute `pnpm build` while `pnpm dev` is active within the workspace, as concurrent builds cause file corruption in Rust artifacts and bundled outputs. Sources: [conductor.json:21-21](https://github.com/blade47/next.js/blob/main/conductor.json#L21-L21) Sources: [conductor.json:1-24](https://github.com/blade47/next.js/blob/main/conductor.json#L1-L24) ## Workspace Root and Lockfile Discovery ### Overview Workspace root and lockfile discovery mechanisms locate project boundaries, detect active package managers, and traverse directory trees to establish correct execution and build roots. Sources: [packages/next/src/lib/find-root.ts:5-65](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts#L5-L65), [packages/next-codemod/lib/handle-package.ts:55-87](https://github.com/blade47/next-codemod/lib/handle-package.ts#L55-L87) ### Workspace Root Discovery and Traversal Algorithm #### Traversal Mechanics The runtime discovery process identifies workspace boundaries by searching upward from the current working directory (`cwd`) for workspace configuration files and lockfiles using `find-up`. Sources: [packages/next/src/lib/find-root.ts:5-32](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts#L5-L32) #### Call-Chain Execution Walkthrough The workspace discovery pipeline executes through the following sequence: 1. `findRootDirAndLockFiles(cwd)` initiates root and lockfile gathering. Sources: [packages/next/src/lib/find-root.ts:34-38](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts#L34-L38) 2. `findWorkRoot(cwd)` runs first, executing an upward search prioritized for `pnpm-workspace.yaml` before checking other lockfile types to prevent accidental inclusion of nested lockfiles. Sources: [packages/next/src/lib/find-root.ts:5-32](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts#L5-L32) 3. If a lockfile or workspace file is found, `findRootDirAndLockFiles` enters a `while (true)` traversal loop, checking parent directories via `dirname(currentDir)` until reaching the filesystem root (`parentDir === currentDir`) or finding additional parent lockfiles. Sources: [packages/next/src/lib/find-root.ts:46-59](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts#L46-L59) 4. Finally, `rootDir` is resolved as `dirname(lockFiles[lockFiles.length - 1])`. Sources: [packages/next/src/lib/find-root.ts:63-63](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts#L63-L63) > [!NOTE] > `findWorkRoot` explicitly checks for `pnpm-workspace.yaml` prior to searching for general lockfiles to ensure that root configuration files take precedence over nested application lockfiles. Sources: [packages/next/src/lib/find-root.ts:6-18](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts#L6-L18) Sources: [packages/next/src/lib/find-root.ts:5-65](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts#L5-L65) ### Multi-Manager Detection and Lockfile Handling Package managers are identified through lockfile inspection or environment variables. The codebase handles multiple package managers and supports duplicate lockfile warnings when nested configurations are detected. Sources: [packages/next-codemod/lib/handle-package.ts:55-87](https://github.com/blade47/next-codemod/lib/handle-package.ts#L55-L87) | Lockfile / Config Name | Detected Package Manager | Sources | | :--- | :--- | :--- | | `package-lock.json` | `npm` | [packages/next-codemod/lib/handle-package.ts:69-70](https://github.com/blade47/next-codemod/lib/handle-package.ts#L69-L70) | | `yarn.lock` | `yarn` | [packages/next-codemod/lib/handle-package.ts:71-72](https://github.com/blade47/next-codemod/lib/handle-package.ts#L71-L72) | | `pnpm-lock.yaml` / `pnpm-workspace.yaml` | `pnpm` | [packages/next-codemod/lib/handle-package.ts:73-74](https://github.com/blade47/next-codemod/lib/handle-package.ts#L73-L74), [packages/next/src/lib/find-root.ts:8-14](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts#L8-L14) | | `bun.lock` / `bun.lockb` | `bun` | [packages/next-codemod/lib/handle-package.ts:75-77](https://github.com/blade47/next-codemod/lib/handle-package.ts#L75-L77) | > [!WARNING] > If multiple lockfiles are detected during traversal (`lockFiles.length > 1`), Next.js emits a warning selecting the topmost lockfile directory as the root and instructing developers to configure `turbopack.root` or `outputFileTracingRoot` to silence the warning. Sources: [packages/next/src/lib/find-root.ts:67-93](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts#L67-L93) Sources: [packages/next/src/lib/find-root.ts:34-94](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-root.ts#L34-L94), [packages/next-codemod/lib/handle-package.ts:55-87](https://github.com/blade47/next-codemod/lib/handle-package.ts#L55-L87) ## Workspace Packages and Applications Structure ### Overview The workspace architecture organizes source code across generation templates, analytical standalone applications, and experimental tracing packages. Scaffolding utilities within `packages/create-next-app` configure project templates dynamically, translating user flags into workspace configurations, package manager rules, and dependency structures. Sources: [packages/create-next-app/templates/index.ts:1-435](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts) ### Template Generation and Package Configuration The template installation process handled by `installTemplate` manages file copying, compiler integration, import alias normalization, and manifest serialization. It maps user-selected bundlers, linters, and package managers directly into `package.json` configurations and workspace files. Sources: [packages/create-next-app/templates/index.ts:48-408](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts) | Configuration Flag | Target File | Modification Logic | Sources | | :--- | :--- | :--- | :--- | | `bundler: Bundler.Rspack` | `next.config.mjs` / `next.config.ts` | Wraps default export with `withRspack(nextConfig)` and injects `next-rspack` dependency | [packages/create-next-app/templates/index.ts:97-110](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L97-L110), [packages/create-next-app/templates/index.ts:258-260](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L258-L260) | | `reactCompiler: true` | `next.config.mjs` / `next.config.ts` | Inserts `reactCompiler: true` into config options and adds `babel-plugin-react-compiler` | [packages/create-next-app/templates/index.ts:112-125](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L112-L125), [packages/create-next-app/templates/index.ts:262-264](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L262-L264) | | `importAlias` | `tsconfig.json` / `jsconfig.json` & Source Files | Replaces `@/*` paths with custom alias and recursively updates matching imports across source files | [packages/create-next-app/templates/index.ts:127-177](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L127-L177) | | `srcDir: true` | Target root directory | Creates `src/` directory, relocates default app directories (`app`, `pages`, `styles`), and updates entry points | [packages/create-next-app/templates/index.ts:179-211](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L179-L211) | | `packageManager: pnpm` | `pnpm-workspace.yaml` | Writes workspace restriction maps such as `allowBuilds` or `ignoredBuiltDependencies` for packages like `sharp` and `unrs-resolver` | [packages/create-next-app/templates/index.ts:333-376](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L333-L376) | > [!WARNING] > When `packageManager` is set to `bun`, the generated manifest automatically populates both `ignoreScripts` and `trustedDependencies` with `sharp` and `unrs-resolver` to suppress installation warnings and satisfy Bun security requirements. Sources: [packages/create-next-app/templates/index.ts:391-402](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L391-L402) Sources: [packages/create-next-app/templates/index.ts:48-408](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L48-L408) ### Bundle Analysis and Binary Data Structures #### ArrayBuffer Ingestion Architecture The workspace includes dedicated analysis tools under `apps/bundle-analyzer` designed to ingest structured build outputs from Rust compilers. The parser processes binary chunks and module graphs via `ModulesData` and `AnalyzeData` classes using `DataView` interfaces over raw ArrayBuffers. Sources: [apps/bundle-analyzer/lib/analyze-data.ts:1-203](https://github.com/blade47/next.js/blob/main/apps/bundle-analyzer/lib/analyze-data.ts#L1-L203) #### Call-Chain Execution Walkthrough The bundle analysis data-loading sequence proceeds as follows: 1. `new ModulesData(modulesArrayBuffer)` receives the raw binary buffer. Sources: [apps/bundle-analyzer/lib/analyze-data.ts:65-65](https://github.com/blade47/next.js/blob/main/apps/bundle-analyzer/lib/analyze-data.ts#L65-L65) 2. `DataView` extracts a 32-bit big-endian integer representing the JSON header length from offset `0`. Sources: [apps/bundle-analyzer/lib/analyze-data.ts:67-68](https://github.com/blade47/next.js/blob/main/apps/bundle-analyzer/lib/analyze-data.ts#L67-L68) 3. `TextDecoder('utf-8')` decodes the subsequent JSON byte range into the `ModulesDataHeader` schema. Sources: [apps/bundle-analyzer/lib/analyze-data.ts:69-75](https://github.com/blade47/next.js/blob/main/apps/bundle-analyzer/lib/analyze-data.ts#L69-L75) 4. The remaining binary segment starting at `4 + modulesJsonLength` is wrapped into a secondary `DataView` (`modulesBinaryData`) for efficient edge-index lookups without full deserialization. Sources: [apps/bundle-analyzer/lib/analyze-data.ts:76-80](https://github.com/blade47/next.js/blob/main/apps/bundle-analyzer/lib/analyze-data.ts#L76-L80) 5. Finally, `pathToModuleIndex` mapping constructs an index lookup table linking file paths to module indices. Sources: [apps/bundle-analyzer/lib/analyze-data.ts:82-92](https://github.com/blade47/next.js/blob/main/apps/bundle-analyzer/lib/analyze-data.ts#L82-L92) > [!NOTE] > Edge relationships such as `moduleDependents`, `asyncModuleDependents`, and `tracedModuleDependents` are read lazily via `readEdgesDataAtIndex` by computing variable-length offset boundaries directly from the binary data view. Sources: [apps/bundle-analyzer/lib/analyze-data.ts:108-198](https://github.com/blade47/next.js/blob/main/apps/bundle-analyzer/lib/analyze-data.ts#L108-L198) Sources: [apps/bundle-analyzer/lib/analyze-data.ts:60-203](https://github.com/blade47/next.js/blob/main/apps/bundle-analyzer/lib/analyze-data.ts#L60-L203) ### Experimental Node Module Tracing Package The `turbopack/packages/node-module-trace` package operates as an experimental dependency file tracing utility within the monorepo structure. Publishing under the `@vercel/experimental-nft` package name with alias `node-file-trace`, it exposes metadata configuration specifying public access and MIT licensing rules. Sources: [turbopack/packages/node-module-trace/package.json:1-10](https://github.com/blade47/next.js/blob/main/turbopack/packages/node-module-trace/package.json#L1-L10) Sources: [turbopack/packages/node-module-trace/package.json:1-10](https://github.com/blade47/next.js/blob/main/turbopack/packages/node-module-trace/package.json#L1-L10) ## Task Automation and Bundle Pipelines ### Overview Task automation and bundle pipelines within the monorepo rely on programmatic taskfile execution and dedicated precompiled dependency distribution pipelines. Sources: [packages/next/taskfile.js:2752-2795](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L2752-L2795) ### Taskfile Execution and Watch Pipelines #### Watch Initialization The default taskfile export initializes a development build lifecycle by clearing the `dist` directory, triggering the main `build` task, and registering active file watchers across source subdirectories. Each watcher maps specific source paths to corresponding compilation targets with development options enabled (`opts = { dev: true }`). Sources: [packages/next/taskfile.js:2752-2755](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L2752-L2755) #### Call-Chain Execution Walkthrough The default watcher registration sequence proceeds as follows: 1. `export default async function (task)` initializes the task context and clears the `dist` target directory via `task.clear('dist')`. Sources: [packages/next/taskfile.js:2752-2754](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L2752-L2754) 2. `task.start('build', opts)` executes the initial compilation pipeline. Sources: [packages/next/taskfile.js:2755-2755](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L2755-L2755) 3. `task.watch('src/bin', 'bin', opts)` registers incremental rebuild bindings for binary CLI entry points. Sources: [packages/next/taskfile.js:2756-2756](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L2756-L2756) 4. `task.watch('src/server', ['server', 'server_esm', 'server_wasm'], opts)` binds server source modifications to CommonJS, ESM, and WebAssembly compilation targets. Sources: [packages/next/taskfile.js:2758-2758](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L2758-L2758) 5. `task.watch('src/shared', [...], opts)` dispatches shared module updates across re-exported, ESM, and standard target pipelines. Sources: [packages/next/taskfile.js:2790-2794](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L2790-L2794) > [!NOTE] > The shared source watcher explicitly excludes test files (`**/*.test.js`, `**/*.test.ts`, `**/*.test.tsx`, `**/*.test.d.ts`) and core configuration modules like `config`, `constants`, `dynamic`, `app-dynamic`, `head`, and `runtime-config`. Sources: [packages/next/taskfile.js:2797-2805](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L2797-L2805) Sources: [packages/next/taskfile.js:2752-2807](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L2752-L2807) ### Precompiled Dependency and Vendor Distribution Pipelines Vendor distribution pipelines manage external packages such as React, React DOM, Scheduler, and PostCSS plugins by copying compiled CommonJS artifacts, rewriting package manifests, and removing redundant distribution files. For instance, `copy_vendor_react` processes experimental or standard channels via `overridePackageName` and `aliasVendoredReactPackages`. Sources: [packages/next/taskfile.js:1435-1493](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1435-L1493) | Item to Remove | Target Directory | Purpose | Sources | | :--- | :--- | :--- | :--- | | `static.js` | `src/compiled/react-dom*` | Removes unused static server entry point | [packages/next/taskfile.js:1620-1626](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1620-L1626) | | `static.browser.js` | `src/compiled/react-dom*` | Removes browser-specific static rendering artifact | [packages/next/taskfile.js:1620-1626](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1620-L1626) | | `unstable_testing.js` | `src/compiled/react-dom*` | Strips unstable testing entry point | [packages/next/taskfile.js:1620-1627](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1620-L1627) | | `test-utils.js` | `src/compiled/react-dom*` | Strips test utility module | [packages/next/taskfile.js:1620-1628](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1620-L1628) | | `server.bun.js` | `src/compiled/react-dom*` | Removes Bun runtime server bundle | [packages/next/taskfile.js:1620-1629](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1620-L1629) | | `unstable_server-external-runtime.js` | `src/compiled/react-dom*` | Removes unstable external runtime helper | [packages/next/taskfile.js:1620-1634](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1620-L1634) | Sources: [packages/next/taskfile.js:1620-1639](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1620-L1639) > [!WARNING] > When compiling error codes via `check_error_codes`, failures in CI environments automatically output a notification instructing developers to run `pnpm build` or `pnpm update-error-codes` to synchronize `errors.json` before forcing a process exit with code `1`. Sources: [packages/next/taskfile.js:2733-2750](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L2733-L2750) Sources: [packages/next/taskfile.js:1435-1639](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L1435-L1639), [packages/next/taskfile.js:2733-2750](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js#L2733-L2750) ## Workspace Diagnostics and Route Analytics ### Overview Monorepo diagnostics and analytics combine command-line inspection utilities, static route measurement, and live Chrome DevTools workspace integration to provide insight into a built application's structure and environment. Sources: [packages/next/src/cli/next-info.ts:1-169](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L1-L169), [packages/next/src/cli/internal/static-routes-info.ts:1-16](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L1-L16) ### Diagnostic Inspection and Environment Reporting The `next info` CLI utility (`printInfo()`) collects environment parameters, binary versions, and package versions to standard output. Sources: [packages/next/src/cli/next-info.ts:96-169](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L96-L169) | Package Key | Resolution Source | Fallback / Default | Sources | | :--- | :--- | :--- | :--- | | `next` | `getPackageVersion('next')` | `'N/A'` | [packages/next/src/cli/next-info.ts:55-61](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L55-L61), [packages/next/src/cli/next-info.ts:97-97](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L97-L97) | | `eslint-config-next` | `getPackageVersion('eslint-config-next')` | `'N/A'` | [packages/next/src/cli/next-info.ts:55-61](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L55-L61), [packages/next/src/cli/next-info.ts:137-137](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L137-L137) | | `react` | `getPackageVersion('react')` | `'N/A'` | [packages/next/src/cli/next-info.ts:55-61](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L55-L61), [packages/next/src/cli/next-info.ts:138-138](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L138-L138) | | `react-dom` | `getPackageVersion('react-dom')` | `'N/A'` | [packages/next/src/cli/next-info.ts:55-61](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L55-L61), [packages/next/src/cli/next-info.ts:139-139](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L139-L139) | | `typescript` | `getPackageVersion('typescript')` | `'N/A'` | [packages/next/src/cli/next-info.ts:55-61](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L55-L61), [packages/next/src/cli/next-info.ts:140-140](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L140-L140) | | `next-rspack` | `getPackageVersion('next-rspack')` (Conditional on `process.env.NEXT_RSPACK`) | `'N/A'` | [packages/next/src/cli/next-info.ts:55-61](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L55-L61), [packages/next/src/cli/next-info.ts:142-145](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L142-L145) | Sources: [packages/next/src/cli/next-info.ts:55-145](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L55-L145) > [!WARNING] > When `printInfo()` checks the package registry for release staleness via `fetch`, network failures do not halt execution. Instead, they emit a yellow-highlighted warning instructing the user to verify against the latest canary release. Sources: [packages/next/src/cli/next-info.ts:105-133](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L105-L133) ### Static Route Analysis Pipeline The `next internal static-routes-info` command performs static bundle size reporting across built routes without executing application code. Sources: [packages/next/src/cli/internal/static-routes-info.ts:1-16](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L1-L16) | Category Constant | Human-Readable Label | Target File Classification | Sources | | :--- | :--- | :--- | :--- | | `clientJs` | Client JS | Client-side JavaScript bundles and chunks | [packages/next/src/cli/internal/static-routes-info.ts:62-62](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L62-L62), [packages/next/src/cli/internal/static-routes-info.ts:73-73](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L73-L73) | | `clientCss` | Client CSS | Client stylesheets | [packages/next/src/cli/internal/static-routes-info.ts:63-63](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L63-L63), [packages/next/src/cli/internal/static-routes-info.ts:74-74](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L74-L74) | | `clientMaps` | Client Source Maps | Client-side source map files (.map) | [packages/next/src/cli/internal/static-routes-info.ts:64-64](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L64-L64), [packages/next/src/cli/internal/static-routes-info.ts:75-75](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L75-L75) | | `serverBundled` | Server Bundled JS | Server-side bundled JavaScript files | [packages/next/src/cli/internal/static-routes-info.ts:65-65](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L65-L65), [packages/next/src/cli/internal/static-routes-info.ts:76-76](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L76-L76) | | `serverUnbundled` | Server Unbundled | Traced external dependencies (e.g., `node_modules`) | [packages/next/src/cli/internal/static-routes-info.ts:66-66](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L66-L66), [packages/next/src/cli/internal/static-routes-info.ts:77-77](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L77-L77) | | `serverMaps` | Server Source Maps | Server-side source map files | [packages/next/src/cli/internal/static-routes-info.ts:67-67](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L67-L67), [packages/next/src/cli/internal/static-routes-info.ts:78-78](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L78-L78) | Sources: [packages/next/src/cli/internal/static-routes-info.ts:61-79](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L61-L79) > [!TIP] > Sorting keys available via `--sort` include `name`, `client`, `client-js`, `client-css`, `client-map`, `server`, `server-bundled-js`, `server-unbundled`, `server-map`, and `total`. Sources: [packages/next/src/cli/internal/static-routes-info.ts:36-47](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L36-L47) ### Chrome DevTools Workspace Integration The server exposes an endpoint to support Chrome DevTools Workspaces via `isChromeDevtoolsWorkspaceUrl(pathname)` matching `/.well-known/appspecific/com.chrome.devtools.json`. Sources: [packages/next/src/server/lib/chrome-devtools-workspace.ts:1-31](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/chrome-devtools-workspace.ts#L1-L31) ```typescript async function getChromeDevtoolsWorkspace( root: string, configDistDir: string ): Promise { if (workspaceUUID === null) { const distDir = path.join(root, configDistDir) const cacheBaseDir = getStorageDirectory(distDir) if (cacheBaseDir === undefined) { workspaceUUID = randomUUID() } else { const cachedUUIDPath = path.join( cacheBaseDir, 'chrome-devtools-workspace-uuid' ) try { workspaceUUID = await fs.promises.readFile(cachedUUIDPath, 'utf8') } catch { workspaceUUID = randomUUID() try { await fs.promises.writeFile(cachedUUIDPath, workspaceUUID, 'utf8') } catch (cause) { console.warn( new Error( 'Failed to persist Chrome DevTools workspace UUID. The Chrome DevTools Workspace needs to be reconnected after the next page reload.', { cause } ) ) } } } } return { workspace: { uuid: workspaceUUID, root, }, } } ``` Sources: [packages/next/src/server/lib/chrome-devtools-workspace.ts:46-89](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/chrome-devtools-workspace.ts#L46-L89) > [!IMPORTANT] > The workspace UUID is held in a module-level variable (`workspaceUUID`) to remain constant throughout the server's lifecycle. Sources: [packages/next/src/server/lib/chrome-devtools-workspace.ts:10-10](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/chrome-devtools-workspace.ts#L10-L10) ## Multi-Workspace Fixtures and Compatibility Matrix ### Fixture System Overview The test fixtures under `packages/next-codemod/bin/__testfixtures__/` supply a matrix of workspace configurations and compatibility scenarios for validating codemod operations across Next.js versions, React major versions, router structures, and package manager feature sets. Sources: [packages/next-codemod/bin/__testfixtures__/next-14-installed/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/next-14-installed/pnpm-workspace.yaml#L1-L1), [packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml:1-4](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml#L1-L4) ### Compatibility Matrix and Workspace Fixture Catalog The test suite organizes fixtures into specific categories reflecting dependency setups, feature flags, and package manager options. Sources: [packages/next-codemod/bin/__testfixtures__/next-14-installed/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/next-14-installed/pnpm-workspace.yaml#L1-L1) | Fixture Path | Focus Area / Scenario | Workspace Configuration | Sources | |---|---|---|---| | `packages/next-codemod/bin/__testfixtures__/next-14-installed/` | Next.js version 14 migration baseline | Empty workspace root | [packages/next-codemod/bin/__testfixtures__/next-14-installed/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/next-14-installed/pnpm-workspace.yaml#L1-L1) | | `packages/next-codemod/bin/__testfixtures__/geo-ip-usage/` | IP geolocation API usage detection | Empty workspace root | [packages/next-codemod/bin/__testfixtures__/geo-ip-usage/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/geo-ip-usage/pnpm-workspace.yaml#L1-L1) | | `packages/next-codemod/bin/__testfixtures__/no-geo-ip-usage/` | Absence of geolocation API usage | Empty workspace root | [packages/next-codemod/bin/__testfixtures__/no-geo-ip-usage/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/no-geo-ip-usage/pnpm-workspace.yaml#L1-L1) | | `packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-pages-router/` | React 18 with Pages router exclusively | Empty workspace root | [packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-pages-router/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-pages-router/pnpm-workspace.yaml#L1-L1) | | `packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-pages-router/` | React 19 with Pages router exclusively | Empty workspace root | [packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-pages-router/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-pages-router/pnpm-workspace.yaml#L1-L1) | | `packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-app-router/` | React 18 with App router exclusively | Empty workspace root | [packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-app-router/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-app-router/pnpm-workspace.yaml#L1-L1) | | `packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-app-router/` | React 19 with App router exclusively | Empty workspace root | [packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-app-router/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-app-router/pnpm-workspace.yaml#L1-L1) | | `packages/next-codemod/bin/__testfixtures__/react-18-installed-mixed-router/` | React 18 with mixed Pages and App routers | Empty workspace root | [packages/next-codemod/bin/__testfixtures__/react-18-installed-mixed-router/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-18-installed-mixed-router/pnpm-workspace.yaml#L1-L1) | | `packages/next-codemod/bin/__testfixtures__/react-19-installed-mixed-router/` | React 19 with mixed Pages and App routers | Empty workspace root | [packages/next-codemod/bin/__testfixtures__/react-19-installed-mixed-router/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-19-installed-mixed-router/pnpm-workspace.yaml#L1-L1) | | `packages/next-codemod/bin/__testfixtures__/suggest-turbopack/` | Turbopack adoption suggestion | Empty workspace root | [packages/next-codemod/bin/__testfixtures__/suggest-turbopack/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/suggest-turbopack/pnpm-workspace.yaml#L1-L1) | | `packages/next-codemod/bin/__testfixtures__/change-turbo-to-turbopack/` | CLI flag migration from turbo to turbopack | Empty workspace root | [packages/next-codemod/bin/__testfixtures__/change-turbo-to-turbopack/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/change-turbo-to-turbopack/pnpm-workspace.yaml#L1-L1) | | `packages/next-codemod/bin/__testfixtures__/peer-dep-out-of-range/` | Peer dependency mismatch scenarios | Empty workspace root | [packages/next-codemod/bin/__testfixtures__/peer-dep-out-of-range/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/peer-dep-out-of-range/pnpm-workspace.yaml#L1-L1) | | `packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/` | PNPM v11 build controls and overrides | Explicit `allowBuilds` configuration | [packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml:1-4](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml#L1-L4) | Sources: [packages/next-codemod/bin/__testfixtures__/next-14-installed/pnpm-workspace.yaml:1-1](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/next-14-installed/pnpm-workspace.yaml#L1-L1), [packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml:1-4](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml#L1-L4) ### PNPM v11 Workspace Build Constraints The `pnpm-v11-overrides` fixture defines explicit package build permissions via the `allowBuilds` mapping in `pnpm-workspace.yaml`. Sources: [packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml:1-4](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml#L1-L4) ```yaml allowBuilds: sharp: false unrs-resolver: false ``` Sources: [packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml:1-4](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml#L1-L4) > [!WARNING] > Setting build permissions to `false` in `allowBuilds` prevents native postinstall compilation scripts from executing for packages such as `sharp` and `unrs-resolver`, which can affect native module loading during workspace test executions. Sources: [packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml:1-4](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/pnpm-v11-overrides/pnpm-workspace.yaml#L1-L4) ## Related - [[Project Structure]] --- ## Technical docs: Server Request Lifecycle URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/server-runtime/server-request-lifecycle
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/base-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts) - [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts) - [packages/next/src/server/lib/router-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-server.ts) - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/server/dev/next-dev-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts) - [packages/next/src/server/next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts) - [packages/next/src/server/web/adapter.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts) - [packages/next/src/server/render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx) - [packages/next/src/server/base-http/node.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts) - [packages/next/src/server/base-http/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/index.ts) - [packages/next/src/server/base-http/web.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts) - [packages/next/src/server/lib/start-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/start-server.ts) - [packages/next/src/server/request-meta.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request-meta.ts) - [packages/next/src/server/image-optimizer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/image-optimizer.ts) - [packages/next/src/server/lib/patch-set-header.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-set-header.ts)
## Overview The server request lifecycle governs how incoming HTTP connections are received, abstracted, processed, and rendered across different runtime environments. It coordinates server initialization, uniform request/response encapsulation, metadata tracking, routing pipelines, and rendering execution for both the App and Pages routers, while providing robust error handling and development-mode compilation hooks. Sources: [packages/next/src/server/base-server.ts:1714-1729](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1714-L1729), [packages/next/src/server/base-http/index.ts:28-103](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/index.ts#L28-L103), [packages/next/src/server/request-meta.ts:51-338](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request-meta.ts#L51-L338), [packages/next/src/server/lib/router-server.ts:371-431](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-server.ts#L371-L431) ## Server Entry and Initialization ### Overview Incoming HTTP connections are received and initialized through `startServer` or wrapped via `NextServer` and `NextCustomServer` classes. The server establishes network listeners, handles port retries and process cleanups, and delegates incoming socket and request events into router handlers. Sources: [packages/next/src/server/lib/start-server.ts:184-295](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/start-server.ts#L184-L295), [packages/next/src/server/next.ts:183-248](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L183-L248) ### Server Initialization and Lifecycle Execution The startup procedure orchestrates socket binding, worker messaging, and configuration parsing through a deterministic call sequence. ```mermaid sequenceDiagram participant CLI as startServer() / Worker participant HTTP as http.createServer() participant INIT as getRequestHandlers() participant ROUTER as initialize() CLI->>HTTP: Create server with requestListener & upgrade handlers HTTP->>CLI: server.listen(port, hostname) CLI->>INIT: Trigger getRequestHandlers() on 'listening' event INIT->>ROUTER: Call initialize(opts) ROUTER--YIELD-->CLI: Return requestHandler, upgradeHandler, server ``` Sources: [packages/next/src/server/lib/start-server.ts:139-178](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/start-server.ts#L139-L178), [packages/next/src/server/lib/start-server.ts:274-321](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/start-server.ts#L274-L321) ### Server Wrapper Implementations Next.js provides distinct server wrappers for production execution (`NextServer`), custom server integrations via `import next from 'next'` (`NextCustomServer`), and worker-based process spawning. | Class / Function | Target Use Case | Key Methods | Sources | |------------------|-----------------|-------------|---------| | `NextServer` | `next start` production server wrapper | `getRequestHandler()`, `getServer()`, `prepare()` | [packages/next/src/server/next.ts:183-423](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L183-L423) | | `NextCustomServer` | Custom servers using `import next from 'next'` | `prepare()`, `getRequestHandler()`, `render()` | [packages/next/src/server/next.ts:425-612](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L425-L612) | | `startServer` | Standalone server lifecycle runner | `requestListener()`, `server.listen()` | [packages/next/src/server/lib/start-server.ts:184-353](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/start-server.ts#L184-L353) | Sources: [packages/next/src/server/next.ts:183-612](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts#L183-L612), [packages/next/src/server/lib/start-server.ts:184-353](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/start-server.ts#L184-L353) > [!NOTE] > During server initialization in development (`isDev`), `startServer` sets up a `Watchpack` instance monitoring configuration files (`CONFIG_FILES`) and distribution directories (`absDistDir`) to trigger an automatic server restart with `RESTART_EXIT_CODE` upon modifications or deletions. Sources: [packages/next/src/server/lib/start-server.ts:557-608](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/start-server.ts#L557-L608) ### Request Dispatched Handlers When an HTTP connection arrives at `requestListener`, requests await the initialization promise (`handlersPromise`) before being passed to `requestHandler`. Upgrade requests are similarly captured and routed through `upgradeHandler`. ```typescript async function requestListener(req: IncomingMessage, res: ServerResponse) { try { if (handlersPromise) { await handlersPromise handlersPromise = undefined } await requestHandler(req, res) } catch (err) { res.statusCode = 500 res.end('Internal Server Error') Log.error(`Failed to handle request for ${req.url}`) console.error(err) } } ``` Sources: [packages/next/src/server/lib/start-server.ts:240-272](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/start-server.ts#L240-L272) ## HTTP Abstraction and Request Adapters ### Overview Next.js unifies request and response handling across Node.js and Web standard runtime environments through abstract base classes (`BaseNextRequest` and `BaseNextResponse`) implemented by concrete runtime wrappers (`NodeNextRequest`, `NodeNextResponse`, `WebNextRequest`, and `WebNextResponse`). This encapsulation allows server pipelines to interact with request streams, headers, cookies, and status codes uniformly regardless of whether execution occurs in a Node.js server environment or a Web-standard edge sandbox. Sources: [packages/next/src/server/base-http/index.ts:28-103](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/index.ts#L28-L103), [packages/next/src/server/base-http/node.ts:19-169](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L19-L169), [packages/next/src/server/base-http/web.ts:11-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L11-L140) ### HTTP Abstraction Architecture The architecture relies on abstract classes that define common properties and shared utility methods like cookie parsing and redirect helpers, while delegating low-level input/output operations to runtime-specific classes. | Abstract Class | Concrete Node.js Class | Concrete Web Class | Destination / Underlying Body Type | Sources | |----------------|------------------------|--------------------|----------------------------------|---------| | `BaseNextRequest` | `NodeNextRequest` | `WebNextRequest` | Node.js `Readable` stream vs Web `ReadableStream | null` | [packages/next/src/server/base-http/index.ts:28-45](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/index.ts#L28-L45), [packages/next/src/server/base-http/node.ts:19-73](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L19-L73), [packages/next/src/server/base-http/web.ts:11-36](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L11-L36) | | `BaseNextResponse` | `NodeNextResponse` | `WebNextResponse` | Node.js `Writable` (`ServerResponse`) vs Web `WritableStream` (`TransformStream`) | [packages/next/src/server/base-http/index.ts:47-103](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/index.ts#L47-L103), [packages/next/src/server/base-http/node.ts:75-169](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L75-L169), [packages/next/src/server/base-http/web.ts:38-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L38-L140) | Sources: [packages/next/src/server/base-http/index.ts:28-103](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/index.ts#L28-L103), [packages/next/src/server/base-http/node.ts:19-169](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L19-L169), [packages/next/src/server/base-http/web.ts:11-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L11-L140) > [!WARNING] > `NodeNextRequest.stream()` can only be called once. Attempting to consume or convert the Node request body into a Web `ReadableStream` more than once throws an invariant error because the underlying Node.js stream begins flowing immediately upon attaching data handlers. Sources: [packages/next/src/server/base-http/node.ts:42-72](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L42-L72) ### Web Response Transformation and Lifecycle In web-standard and edge runtimes, `WebNextResponse` manages output through a `TransformStream` and a `CloseController`. When completing execution, `toResponse()` resolves pending promises, wraps body consumption if listeners are present, and returns a standard Web `Response`. ```typescript export class WebNextResponse extends BaseNextResponse { private headers = new Headers() private textBody: string | undefined = undefined private closeController = new CloseController() public statusCode: number | undefined public statusMessage: string | undefined constructor(public transformStream = new TransformStream()) { super(transformStream.writable) } setHeader(name: string, value: string | string[]): this { this.headers.delete(name) for (const val of Array.isArray(value) ? value : [value]) { this.headers.append(name, val) } return this } } ``` Sources: [packages/next/src/server/base-http/web.ts:38-58](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L38-L58) > [!CAUTION] > Calling `onClose()` on a `WebNextResponse` instance that is already closed triggers an `InvariantError`. Lifecycle callbacks must be registered before the response body finishes streaming and the close controller dispatches its close event. Sources: [packages/next/src/server/base-http/web.ts:132-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L132-L140) ## Request Metadata and State Tracking ### Overview Next.js attaches routing context, parsed parameters, incremental cache references, and body cloning state directly to incoming HTTP request objects using a private symbol key. This avoids polluting public request properties while allowing internal modules to share request-scoped data across boundaries. ```typescript export const NEXT_REQUEST_META = Symbol.for('NextInternalRequestMeta') export type NextIncomingMessage = ( | BaseNextRequest | IncomingMessage | NextRequest ) & { [NEXT_REQUEST_META]?: RequestMeta } ``` Sources: [packages/next/src/server/request-meta.ts:19-27](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request-meta.ts#L19-L27) ### Request Metadata Operations State attachment and retrieval are handled by helper functions that read or mutate the record stored under `NEXT_REQUEST_META`. ```typescript export function getRequestMeta( req: NextIncomingMessage, key?: undefined ): RequestMeta export function getRequestMeta( req: NextIncomingMessage, key: K ): RequestMeta[K] export function getRequestMeta( req: NextIncomingMessage, key?: K ): RequestMeta | RequestMeta[K] { const meta = req[NEXT_REQUEST_META] || {} return typeof key === 'string' ? meta[key] : meta } export function setRequestMeta(req: NextIncomingMessage, meta: RequestMeta) { req[NEXT_REQUEST_META] = meta return meta } export function addRequestMeta( request: NextIncomingMessage, key: K, value: RequestMeta[K] ) { const meta = getRequestMeta(request) meta[key] = value return setRequestMeta(request, meta) } export function removeRequestMeta( request: NextIncomingMessage, key: K ) { const meta = getRequestMeta(request) delete meta[key] return setRequestMeta(request, meta) } ``` Sources: [packages/next/src/server/request-meta.ts:348-408](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request-meta.ts#L348-L408) ### Header Patching and Cookie Support To prevent headers set by middleware from being overwritten or dropped by downstream API routes or `getServerSideProps`, Next.js patches the response object's `setHeader` method. ```typescript export function patchSetHeaderWithCookieSupport( req: NextIncomingMessage, res: PatchableResponse ) { if (res[PATCHED_SET_HEADER]) { return } const setHeader = res.setHeader.bind(res) Object.defineProperty(res, PATCHED_SET_HEADER, { value: true, }) res.setHeader = ( name: string, value: string | string[] ): PatchableResponse => { if ('headersSent' in res && res.headersSent) { return res } if (name.toLowerCase() === 'set-cookie') { const middlewareValue = getRequestMeta(req, 'middlewareCookie') if ( !middlewareValue || !Array.isArray(value) || !value.every((item, idx) => item === middlewareValue[idx]) ) { value = [ ...new Set([ ...(middlewareValue || []), ...(typeof value === 'string' ? [value] : Array.isArray(value) ? value : []), ]), ] } } return setHeader(name, value) } } ``` Sources: [packages/next/src/server/lib/patch-set-header.ts:18-66](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-set-header.ts#L18-L66) > [!NOTE] > `patchSetHeaderWithCookieSupport` uses a symbol flag `PATCHED_SET_HEADER` to guarantee that the response object is patched at most once per request, avoiding recursive wrapper overhead when multiple handlers invoke `setHeader`. Sources: [packages/next/src/server/lib/patch-set-header.ts:3-24](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-set-header.ts#L3-L24) ## Server Request Pipeline (`NextServer` / `NextNodeServer`) ### Overview Server classes coordinate the core request handling loop, URL normalization, locale analysis, and routing execution for Next.js applications. Incoming requests flow through normalization routines that filter pathname information, inspect specialized request markers, and execute tracing spans. Sources: [packages/next/src/server/base-server.ts:1637-1661](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1637-L1661), [packages/next/src/server/base-server.ts:1747-1764](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1747-L1764) ### Pathname Normalization and Metadata Pathnames are normalized using an ordered array of normalizers checked sequentially. If a normalizer matches the pathname, its normalization logic is applied directly. ```typescript private normalize = (pathname: string) => { const normalizers: Array = [] if (this.normalizers.data) { normalizers.push(this.normalizers.data) } // We have to put the segment prefetch normalizer before the RSC normalizer // because the RSC normalizer will match the prefetch RSC routes too. if (this.normalizers.segmentPrefetchRSC) { normalizers.push(this.normalizers.segmentPrefetchRSC) } if (this.normalizers.rsc) { normalizers.push(this.normalizers.rsc) } for (const normalizer of normalizers) { if (!normalizer.match(pathname)) continue return normalizer.normalize(pathname, true) } return pathname } ``` Sources: [packages/next/src/server/base-server.ts:1637-1661](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1637-L1661) > [!CAUTION] > The segment prefetch normalizer must be evaluated before the React Server Components (RSC) normalizer in the array. Because the RSC normalizer matches prefetch RSC routes as well, placing it first would intercept and misroute segment prefetches. Sources: [packages/next/src/server/base-server.ts:1644-1648](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1644-L1648) ### Request Pipeline Execution Walkthrough The request handling pipeline coordinates tracing spans, streaming context construction, and execution routing: 1. `run()` / `runImpl()`: Wraps execution within tracing spans and dispatches the catch-all render request handler. 2. `pipe()` / `pipeImpl()`: Inspects the user-agent header, creates a cloned `RequestContext` with `supportsDynamicResponse` based on bot detection status, and determines whether streaming metadata should be served. 3. `normalizeAndAttachMetadata()`: Evaluates specialized request handlers like image optimization and pages data endpoints before standard routing. Sources: [packages/next/src/server/base-server.ts:1663-1676](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1663-L1676), [packages/next/src/server/base-server.ts:1747-1763](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1747-L1763), [packages/next/src/server/base-server.ts:1765-1804](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1765-L1804) ### Pipeline Constants and Design Choices | Design Choice | Benefit | Cost | | --- | --- | --- | | Sequential normalizer array evaluation | Deterministic matching order for overlapping patterns like RSC and segment prefetches | Linear search overhead across registered normalizers per request | | Centralized tracing via `BaseServerSpan` | Detailed OpenTelemetry instrumentation across request lifecycles | Additional wrapper allocation per pipeline phase | | Lazy instrumentation loading during `prepare()` | Avoids blocking server startup when instrumentation is absent | Deferred module resolution check until initialization completes | Sources: [packages/next/src/server/base-server.ts:1637-1661](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1637-L1661), [packages/next/src/server/base-server.ts:1714-1728](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1714-L1728), [packages/next/src/server/base-server.ts:1751-1754](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1751-L1754) ## NextNodeServer Dispatch and Route Execution ### Overview NextNodeServer orchestrates concrete request routing and dispatch for Pages API routes, internal error pages, and image optimization pipelines. When an incoming request targets an API endpoint, `handleApiRequest` delegates execution directly to `runApi`. Similarly, internal rendering errors or specialized fallback routes trigger specific handler branches like `renderErrorToResponseImpl`, which evaluates edge function availability for special entries such as `UNDERSCORE_NOT_FOUND_ROUTE_ENTRY`. Sources: [packages/next/src/server/next-server.ts:1232-1239](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1232-L1239), [packages/next/src/server/next-server.ts:1356-1384](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1356-L1384) ### Image Optimization Dispatch and Cache Flow The image optimization subsystem processes requests via `ImageOptimizerCache` and `imageOptimizer`. It validates parameters, manages LRU disk caches or custom cache handlers, and executes image transformations. ```typescript export async function imageOptimizer( imageUpstream: ImageUpstream, paramsResult: Pick, nextConfig: { ... }, opts: { isDev?: boolean; silent?: boolean; previousCacheEntry?: IncrementalResponseCacheEntry | null } ) { const { href, quality, width, mimeType } = paramsResult const { buffer: upstreamBuffer, etag: upstreamEtag } = imageUpstream const maxAge = Math.max(nextConfig.images.minimumCacheTTL, getMaxAge(imageUpstream.cacheControl)) // Detects content type, validates SVG policies, handles animated/bypass types, and optimizes buffer } ``` Sources: [packages/next/src/server/image-optimizer.ts:1056-1095](https://github.com/blade47/next.js/blob/main/packages/next/src/server/image-optimizer.ts#L1056-L1095) ### Image Optimization Execution Walkthrough The image request processing pipeline moves through specific validation, fetching, and transformation stages: 1. `ImageOptimizerCache.validateParams()`: Inspects query parameters `url`, `w`, and `q`, verifying domain whitelist rules, local patterns, and numerical size constraints. 2. `fetchExternalImage()` / `fetchInternalImage()`: Fetches the upstream resource while checking IP restrictions, response body size limits, and redirect thresholds. 3. `imageOptimizer()`: Inspects magic numbers via `detectContentType()`, checks SVG permissions, bypasses animated or raw image types, and runs `optimizeImage()` using Sharp. 4. `sendResponse()`: Sets response headers such as `Cache-Control`, `Vary: Accept`, `Content-Disposition`, and `X-Nextjs-Cache` before streaming the optimized buffer. Sources: [packages/next/src/server/image-optimizer.ts:387-549](https://github.com/blade47/next.js/blob/main/packages/next/src/server/image-optimizer.ts#L387-L549), [packages/next/src/server/image-optimizer.ts:872-1054](https://github.com/blade47/next.js/blob/main/packages/next/src/server/image-optimizer.ts#L872-L1054), [packages/next/src/server/image-optimizer.ts:1056-1236](https://github.com/blade47/next.js/blob/main/packages/next/src/server/image-optimizer.ts#L1056-L1236), [packages/next/src/server/image-optimizer.ts:1292-1327](https://github.com/blade47/next.js/blob/main/packages/next/src/server/image-optimizer.ts#L1292-L1327) ### Image Optimization Constants and Validation Flags | Constant / Array | Values / Types | Purpose | | --- | --- | --- | | `ANIMATABLE_TYPES` | `['image/webp', 'image/png', 'image/gif']` | Formats checked for animation frames that bypass optimization | | `BYPASS_TYPES` | `['image/svg+xml', 'image/x-icon', 'image/x-icns', 'image/bmp', 'image/jxl', 'image/heic']` | Formats returned directly without running the sharp transformer | | `CACHE_VERSION` | `4` | Version identifier embedded into image cache key generation hashes | | `BLUR_IMG_SIZE` | `8` | Default width assigned to generated blur placeholder images | Sources: [packages/next/src/server/image-optimizer.ts:42-60](https://github.com/blade47/next.js/blob/main/packages/next/src/server/image-optimizer.ts#L42-L60) > [!WARNING] > Requesting an external image whose hostname resolves to a private IP address will trigger a 400 error and throw an `ImageError`, unless `images.dangerouslyAllowLocalIP` is explicitly enabled in configuration. Sources: [packages/next/src/server/image-optimizer.ts:878-900](https://github.com/blade47/next.js/blob/main/packages/next/src/server/image-optimizer.ts#L878-L900) ## Page and App Render Dispatch ### Overview The rendering dispatcher coordinates payload generation across both the Pages router (via React Fizz/SSR and `renderToHTMLImpl`) and the App Router (via React Flight streaming and server component rendering trees). The server pipeline constructs execution contexts, evaluates headers such as `NEXT_ROUTER_PREFETCH_HEADER` and `RSC_HEADER`, and delegates execution down to specific module renderers. Sources: [packages/next/src/server/base-server.ts:1765-1804](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1765-L1804), [packages/next/src/server/app-render/app-render.tsx:395-470](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L395-L470), [packages/next/src/server/render.tsx:459-468](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L459-L468) ### Header Parsing and Request Modifiers When a rendering request hits the server, `parseRequestHeaders()` inspects incoming HTTP headers to establish the exact rendering intent, distinguishing between standard RSC navigation, prefetch variants, and HMR refreshes. ```typescript function parseRequestHeaders( headers: IncomingHttpHeaders, options: ParseRequestHeadersOptions ): ParsedRequestHeaders { const isPrefetchRequest = headers[NEXT_ROUTER_PREFETCH_HEADER] === '1' const isAppShellPrefetchRequest = headers[NEXT_ROUTER_PREFETCH_HEADER] === '3' const isRuntimePrefetchRequest = headers[NEXT_ROUTER_PREFETCH_HEADER] === '2' || isAppShellPrefetchRequest const isHmrRefresh = headers[NEXT_HMR_REFRESH_HEADER] !== undefined const isRSCRequest = isRSCRequestHeader(headers[RSC_HEADER]) // ... ``` Sources: [packages/next/src/server/app-render/app-render.tsx:395-414](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L395-L414) ### Render Options and Pipeline Execution The `pipe()` and `pipeImpl()` methods manage request context construction, appending bot detection parameters and streaming metadata flags before executing the render delegate. ```typescript private async pipeImpl( fn: ( ctx: RequestContext ) => Promise, partialContext: Omit< RequestContext, 'renderOpts' > ): Promise { const ua = partialContext.req.headers['user-agent'] || '' const ctx: RequestContext = { ...partialContext, renderOpts: { ...this.renderOpts, supportsDynamicResponse: !this.renderOpts.botType, serveStreamingMetadata: shouldServeStreamingMetadata( ua, this.nextConfig.htmlLimitedBots ), }, } const payload = await fn(ctx) ``` Sources: [packages/next/src/server/base-server.ts:1779-1803](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1779-L1803) ### Parsed Request Header Constants | Header Constant | Header Name / Value Condition | Purpose | | --- | --- | --- | | `NEXT_ROUTER_PREFETCH_HEADER` | `'1'` | Identifies standard client-side router prefetch requests | | `NEXT_ROUTER_PREFETCH_HEADER` | `'2'` | Identifies runtime prefetch requests | | `NEXT_ROUTER_PREFETCH_HEADER` | `'3'` | Identifies App Shell prefetch (omits link data and parameters) | | `NEXT_HMR_REFRESH_HEADER` | `undefined` check | Detects HMR refresh events during development rendering | | `RSC_HEADER` | Checked via `isRSCRequestHeader()` | Identifies React Server Component payload streams | Sources: [packages/next/src/server/app-render/app-render.tsx:401-413](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L401-L413) ### Render Pipeline Design Trade-Offs | Design Choice | Benefit | Cost | | --- | --- | --- | | Separate `renderToHTMLImpl` and Flight stream handlers | Isolates Pages router Fizz/SSR rendering logic from App Router streaming architectures | Duplicates context preparation wiring across different routing paradigms | | Strict header-based prefetch classification (`1`, `2`, `3`) | Prevents unnecessary data resolution during shell and runtime prefetches | Requires careful client/server header synchronization | | Dynamic stale time tree-walking (`getDynamicStaleTime`) | Avoids full server component rendering when extracting static segment configs | Recursively inspects module definitions prior to handling requests | Sources: [packages/next/src/server/app-render/app-render.tsx:482-512](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L482-L512), [packages/next/src/server/render.tsx:459-468](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L459-L468) > [!NOTE] > App Shell prefetches (where `NEXT_ROUTER_PREFETCH_HEADER` is set to `'3'`) omit dynamic parameter resolution during server rendering. Any attempt to await `params` inside an App Shell prefetch will hang indefinitely, producing a param-independent shell of the route. Sources: [packages/next/src/server/app-render/app-render.tsx:380-385](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L380-L385), [packages/next/src/server/app-render/app-render.tsx:403-404](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L403-L404) ## Development Server and Error Handling ### Overview Development-mode execution in Next.js wraps standard request routing with performance instrumentation, memory tracking, on-demand compilation triggers, and enhanced error stack formatting. Development server implementations override core request handlers to ensure bundler service integration and error overlays operate transparently during local development. Sources: [packages/next/src/server/dev/next-dev-server.ts:570-591](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts#L570-L591), [packages/next/src/server/dev/next-dev-server.ts:624-642](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts#L624-L642) ### Dev-Mode Request Interception and Telemetry Incoming requests in the development server pass through `handleRequest()`, which initiates a performance trace span (`handle-request`), waits for server readiness via `this.ready?.promise`, and attaches debug flags such as `PagesErrorDebug`. Upon completion, RSS and heap metrics are captured via `process.memoryUsage()` and logged as child telemetry spans. ```typescript public async handleRequest( req: NodeNextRequest, res: NodeNextResponse, parsedUrl?: NextUrlWithParsedQuery ): Promise { const span = trace('handle-request', undefined, { url: req.url }) const result = await span.traceAsyncFn(async () => { await this.ready?.promise addRequestMeta(req, 'PagesErrorDebug', this.renderOpts.ErrorDebug) return await super.handleRequest(req, res, parsedUrl) }) const memoryUsage = process.memoryUsage() span .traceChild('memory-usage', { url: req.url, 'memory.rss': String(memoryUsage.rss), 'memory.heapUsed': String(memoryUsage.heapUsed), 'memory.heapTotal': String(memoryUsage.heapTotal), }) .stop() return result } ``` Sources: [packages/next/src/server/dev/next-dev-server.ts:570-591](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts#L570-L591) > [!WARNING] > Unhandled errors thrown during `run()` in development servers are passed through `getProperError()`, formatted via `formatServerError()`, and dispatched to `logErrorWithOriginalStack()` before triggering `renderError()`. If headers have already been sent, the response status is forced to `500` with an emergency text fallback. Sources: [packages/next/src/server/dev/next-dev-server.ts:624-642](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts#L624-L642) ### Instrumentation Hooks and On-Demand Compilation The development server manages module compilation and telemetry initialization through lifecycle hooks. `loadInstrumentationModule()` checks for the presence of an instrumentation hook file, ensures it is compiled via `ensurePage()`, and retrieves the module via `getInstrumentationModule()`. ```typescript protected async loadInstrumentationModule(): Promise { let instrumentationModule: any if ( this.actualInstrumentationHookFile && (await this.ensurePage({ page: this.actualInstrumentationHookFile!, clientOnly: false, definition: undefined, }) .then(() => true) .catch(() => false)) ) { try { instrumentationModule = await getInstrumentationModule( this.dir, this.nextConfig.distDir ) } catch (err: any) { err.message = `An error occurred while loading instrumentation hook: ${err.message}` throw err } } return instrumentationModule } ``` Sources: [packages/next/src/server/dev/next-dev-server.ts:714-737](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts#L714-L737) ### Development Server Methods and Fallback Signatures | Method Name | Return Type | Purpose | | --- | --- | --- | | `handleRequest` | `Promise` | Traces dev requests, verifies server readiness, and appends memory usage spans | | `run` | `Promise` | Strips basePath prefixes, dispatches execution to `super.run`, and catches unhandled render errors | | `logErrorWithOriginalStack` | `void` | Delegates stack frame mapping to `this.bundlerService` | | `loadInstrumentationModule` | `Promise` | Ensures and loads the user instrumentation file from the distribution directory | | `ensureEdgeFunction` | `Promise` | Triggers on-demand compilation for Edge runtime worker targets | Sources: [packages/next/src/server/dev/next-dev-server.ts:570-591](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts#L570-L591), [packages/next/src/server/dev/next-dev-server.ts:644-649](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts#L644-L649), [packages/next/src/server/dev/next-dev-server.ts:714-759](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts#L714-L759) ## Related - [[Routing and Normalization]] - [[Route Matching]] - [[App Server Rendering]] --- ## Technical docs: Routing and Normalization URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/server-runtime/routing-and-normalization
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts) - [packages/next/src/server/base-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts) - [packages/next-routing/src/next-data.ts](https://github.com/blade47/next-routing/src/next-data.ts) - [packages/next/src/server/normalizers/locale-route-normalizer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/locale-route-normalizer.ts) - [packages/next/src/server/server-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/server-utils.ts) - [packages/next/src/shared/lib/i18n/normalize-locale-path.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/i18n/normalize-locale-path.ts) - [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts) - [packages/next/src/shared/lib/router/router.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/router.ts) - [packages/next/src/server/web/next-url.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/next-url.ts) - [packages/next-routing/src/resolve-routes.ts](https://github.com/blade47/next-routing/src/resolve-routes.ts) - [packages/next/src/server/lib/router-utils/resolve-routes.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts) - [packages/eslint-plugin-next/src/utils/url.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/utils/url.ts) - [packages/next/src/shared/lib/router/utils/format-next-pathname-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/format-next-pathname-info.ts) - [packages/next/src/shared/lib/normalized-asset-prefix.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/normalized-asset-prefix.ts) - [packages/next/src/shared/lib/router/utils/app-paths.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/app-paths.ts) - [packages/next/src/server/normalizers/request/segment-prefix-rsc.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/segment-prefix-rsc.ts) - [packages/next/src/shared/lib/page-path/normalize-data-path.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/page-path/normalize-data-path.ts) - [packages/next/src/client/normalize-locale-path.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/normalize-locale-path.ts) - [packages/next/src/server/route-modules/app-page/normalize-request-url.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-page/normalize-request-url.ts) - [packages/next-routing/src/i18n.ts](https://github.com/blade47/next-routing/src/i18n.ts) - [packages/next/src/server/normalizers/request/prefix.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/prefix.ts) - [packages/next/src/server/normalizers/request/next-data.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/next-data.ts) - [packages/next/src/server/route-modules/app-route/helpers/clean-url.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/helpers/clean-url.ts) - [packages/next/src/server/lib/i18n-provider.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/i18n-provider.ts) - [packages/next/src/server/normalizers/built/app/app-bundle-path-normalizer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/built/app/app-bundle-path-normalizer.ts) - [packages/next/src/server/normalizers/normalizer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/normalizer.ts) - [packages/next/src/server/request/pathname.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/pathname.ts) - [packages/next/src/client/remove-locale.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/remove-locale.ts) - [packages/next/src/server/normalizers/prefixing-normalizer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/prefixing-normalizer.ts) - [packages/next/src/server/normalizers/request/pathname-normalizer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/pathname-normalizer.ts)
## Overview Routing and Normalization in Next.js serves as the foundational canonicalization engine responsible for parsing, validating, and transforming incoming HTTP request URLs, client transitions, and internal build artifacts before they reach route matchers, middleware, or page renderers. Incoming requests often carry environmental artifacts such as configured basePath prefixes, internationalization locale segments, internal Next.js data request structures, React Server Component extensions, and malformed slashes. Sources: [packages/next/src/server/lib/router-utils/resolve-routes.ts:153-165](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L153-L165) The normalization subsystem addresses these issues by executing structured extraction pipelines across both client and server boundaries, decoupling structural concerns via specialized normalizer classes and utility functions. Sources: [packages/next/src/server/base-server.ts:1637-1661](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1637-L1661) ```mermaid flowchart TD A["Raw Request URL"] --> B["Normalize Slashes"] B --> C["Extract BasePath"] C --> D["Extract Next Data URL"] D --> E["Analyze Locale"] E --> F["Pathname Normalizer Pipeline"] F --> G["Route Matcher / Handler"] ``` Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:63-109](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L63-L109) ## Pathname Information Extraction (`getNextPathnameInfo`) The core analytical primitive for parsing request paths is `getNextPathnameInfo`, located in `packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts`. This utility inspects a raw pathname string against configuration parameters and extracts metadata into an interface called `NextPathnameInfo`. Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:53-61](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L53-L61) The extraction routine checks trailing slashes, strips basePath prefixes, parses Next.js data URLs beginning with `/_next/data/`, and detects locales via `normalizeLocalePath` or `i18nProvider.analyze`. Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:62-111](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L62-L111) ```typescript export interface NextPathnameInfo { basePath?: string buildId?: string locale?: string pathname: string trailingSlash?: boolean } ``` Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:6-30](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L6-L30) > [!NOTE] > When parsing Next.js data URLs, `getNextPathnameInfo` retains the original data prefix for metadata extraction while providing a normalized inner pathname when `parseData: true` is set. Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:83-88](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L83-L88) ## Pathname Normalizers and Object-Oriented Hierarchy Next.js implements an extensible object-oriented normalizer pattern defined by the `Normalizer` and `PathnameNormalizer` interfaces. Sources: [packages/next/src/server/normalizers/normalizer.ts:1-3](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/normalizer.ts#L1-L3) These classes encapsulate specific URL manipulation behaviors, such as stripping base paths, handling `.json` data suffixes, segment prefetch RSC extensions, and bundle path transformations. Sources: [packages/next/src/server/normalizers/request/pathname-normalizer.ts:1-6](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/pathname-normalizer.ts#L1-L6) ```mermaid classDiagram class Normalizer { <> +normalize(pathname: string): string } class PathnameNormalizer { <> +match(pathname: string): boolean +normalize(pathname: string, matched?: boolean): string } class PrefixPathnameNormalizer { -prefix: string +match(pathname: string): boolean +normalize(pathname: string, matched?: boolean): string } class SuffixPathnameNormalizer { -suffix: string +match(pathname: string): boolean +normalize(pathname: string, matched?: boolean): string } class NextDataPathnameNormalizer { -prefix: PrefixPathnameNormalizer -suffix: SuffixPathnameNormalizer +match(pathname: string): boolean +normalize(pathname: string, matched?: boolean): string } class LocaleRouteNormalizer { -provider: I18NProvider +normalize(pathname: string): string } Normalizer <|-- PathnameNormalizer Normalizer <|-- PrefixPathnameNormalizer Normalizer <|-- SuffixPathnameNormalizer Normalizer <|-- LocaleRouteNormalizer PathnameNormalizer <|-- NextDataPathnameNormalizer ``` Sources: [packages/next/src/server/normalizers/request/next-data.ts:7-31](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/next-data.ts#L7-L31) The server instance maintains a prioritized array of normalizers in `BaseServer.normalize` to evaluate requests sequentially. Sources: [packages/next/src/server/base-server.ts:1637-1661](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1637-L1661) Each normalizer implements a guard method `match(pathname)` prior to executing `normalize(pathname)`. For instance, `PrefixPathnameNormalizer` explicitly validates constructor parameters. Sources: [packages/next/src/server/normalizers/request/prefix.ts:3-10](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/prefix.ts#L3-L10) ## Internationalization (`i18n`) Locale Detection and Path Normalization Internationalization routing requires detecting locales from the request pathname, subdomains, cookies, or `Accept-Language` headers, and normalizing the path by stripping the locale prefix. Sources: [packages/next-routing/src/i18n.ts:196-269](https://github.com/blade47/next-routing/src/i18n.ts#L196-L269) This is handled by `normalizeLocalePath`, `I18NProvider`, and routing resolvers. Sources: [packages/next/src/shared/lib/i18n/normalize-locale-path.ts:22-61](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/i18n/normalize-locale-path.ts#L22-L61) The `normalizeLocalePath` function splits the pathname by `/` to inspect the second segment, utilizing a `WeakMap` cache for performance. Sources: [packages/next/src/shared/lib/i18n/normalize-locale-path.ts:8-34](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/i18n/normalize-locale-path.ts#L8-L34) ```typescript const cache = new WeakMap() export function normalizeLocalePath( pathname: string, locales?: readonly string[] ): PathLocale { if (!locales) return { pathname } let lowercasedLocales = cache.get(locales) if (!lowercasedLocales) { lowercasedLocales = locales.map((locale) => locale.toLowerCase()) cache.set(locales, lowercasedLocales) } const segments = pathname.split('/', 2) if (!segments[1]) return { pathname } const segment = segments[1].toLowerCase() const index = lowercasedLocales.indexOf(segment) if (index < 0) return { pathname } const detectedLocale = locales[index] pathname = pathname.slice(detectedLocale.length + 1) || '/' return { pathname, detectedLocale } } ``` Sources: [packages/next/src/shared/lib/i18n/normalize-locale-path.ts:10-61](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/i18n/normalize-locale-path.ts#L10-L61) > [!WARNING] > When `i18n` is configured, failing to account for locale prefixes when resolving filesystem items can result in route matching collisions or 404 errors, as app directory routes do not match i18n locale prefixes directly. Sources: [packages/next/src/server/lib/router-utils/resolve-routes.ts:328-334](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L328-L334) ## Next.js Data URL Normalization (`/_next/data/`) Client-side data fetching for Static Generation and Server-Side Rendering utilizes JSON data files mapped under `/_next/data/{buildId}/{path}.json`. Sources: [packages/next-routing/src/next-data.ts:5-34](https://github.com/blade47/next-routing/src/next-data.ts#L5-L34) The normalization engine converts these data request URLs back into standard page pathnames for routing and execution. Sources: [packages/next/src/shared/lib/page-path/normalize-data-path.ts:6-18](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/page-path/normalize-data-path.ts#L6-L18) Both client-side utilities and server-side router utils perform this canonicalization via `normalizeDataPath`: Sources: [packages/next-routing/src/next-data.ts:40-67](https://github.com/blade47/next-routing/src/next-data.ts#L40-L67) ```typescript export function normalizeDataPath(pathname: string) { if (!pathHasPrefix(pathname || '/', '/_next/data')) { return pathname } pathname = pathname .replace(/\/_next\/data\/[^/]{1,}/, '') .replace(/\.json$/, '') if (pathname === '/index') { return '/' } return pathname } ``` Sources: [packages/next/src/shared/lib/page-path/normalize-data-path.ts:6-18](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/page-path/normalize-data-path.ts#L6-L18) Conversely, denormalization reconstructs the data URL format by injecting the build ID and `.json` extension. Sources: [packages/next-routing/src/next-data.ts:37-67](https://github.com/blade47/next-routing/src/next-data.ts#L37-L67) ## App Directory Route Normalization (`normalizeAppPath`) In the App Router (`app/` directory), file paths on disk include structural syntax such as route groups, parallel route slots, and leaf filenames. Sources: [packages/eslint-plugin-next/src/utils/url.ts:37-61](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/utils/url.ts#L37-L61) The `normalizeAppPath` function strips these markers to derive the public request pathname. Sources: [packages/next/src/shared/lib/router/utils/app-paths.ts:23-52](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/app-paths.ts#L23-L52) ```typescript export function normalizeAppPath(route: string) { return ensureLeadingSlash( route.split('/').reduce((pathname, segment, index, segments) => { if (!segment) { return pathname } if (isGroupSegment(segment)) { return pathname } if (segment[0] === '@') { return pathname } if ( (segment === 'page' || segment === 'route') && index === segments.length - 1 ) { return pathname } return `${pathname}/${segment}` }, '') ) } ``` Sources: [packages/next/src/shared/lib/router/utils/app-paths.ts:23-52](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/app-paths.ts#L23-L52) To correctly prioritize parallel slot paths during route matching and manifest loading, Next.js employs `compareAppPaths`. Sources: [packages/next/src/shared/lib/router/utils/app-paths.ts:64-70](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/app-paths.ts#L64-L70) > [!IMPORTANT] > `compareAppPaths` ensures that parallel slot paths containing `/@` sort before children page paths. Without this, route group prefixes like `(group)` would sort before `@`, leading to manifest mismatches in development mode. Sources: [packages/next/src/shared/lib/router/utils/app-paths.ts:54-70](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/app-paths.ts#L54-L70) ## Request URL Canonicalization Pipeline When an incoming HTTP request is received by the Next.js server, it undergoes a rigorous validation and normalization pipeline before middleware or route handlers execute. Sources: [packages/next/src/server/lib/router-utils/resolve-routes.ts:118-142](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L118-L142) The pipeline checks for repeated slashes and backslashes, constructs absolute initialization URLs, peels locale and basePath prefixes, and tags data requests. Sources: [packages/next/src/server/lib/router-utils/resolve-routes.ts:153-207](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L153-L207) ```mermaid sequenceDiagram participant Client participant Server as BaseServer / resolve-routes participant Normalizer as PathnameNormalizer participant Router as Route Matcher Client->>Server: HTTP Request Server->>Server: Check repeated slashes / backslashes alt Malformed Slashes Detected Server-->>Client: 308 Redirect else Clean URL Server->>Normalizer: Match & Normalize BasePath / Data / Locale Normalizer-->>Server: Canonical Pathname Server->>Router: Resolve Route Match / Middleware end ``` Sources: [packages/next/src/server/lib/router-utils/resolve-routes.ts:153-165](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L153-L165) ## Configuration Options and Reference Table The routing and normalization subsystem relies on configuration properties defined in `next.config.js` and internal request headers. Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:32-51](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L32-L51) | Configuration Property | Type | Default | Purpose / Behavior | | :--- | :--- | :--- | :--- | | `basePath` | `string` | `''` | Prefixes all application routes. | | `i18n` | `object` | `null` | Enables internationalization routing. | | `trailingSlash` | `boolean` | `false` | Enforces or strips trailing slashes. | | `assetPrefix` | `string` | `''` | CDN or asset prefix URL for chunks. | | `skipProxyUrlNormalize` | `boolean` | `false` | Bypasses automatic trailing slash normalization. | Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:40-44](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L40-L44), [packages/next/src/server/lib/router-utils/resolve-routes.ts:193-202](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L193-L202) ## Design Trade-offs The routing and normalization architecture balances performance, flexibility, and compliance with HTTP standards through deliberate design choices. Sources: [packages/next/src/server/base-server.ts:1637-1661](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1637-L1661) | Design Choice | Benefit | Cost / Trade-off | | :--- | :--- | :--- | | **WeakMap Locale Caching** | Prevents redundant lowercasing of locale arrays while allowing GC. | Small memory overhead per unique locale array reference. | | **Pipelined Pathname Normalizers** | Decouples concerns into single-responsibility classes. | Multiple regex checks and string slicing operations per request. | | **Strict App Path Normalization** | Strips layout, group, and slot segments for clean URLs. | Requires manifest lookups to map public paths back to disk. | Sources: [packages/next/src/shared/lib/i18n/normalize-locale-path.ts:8-11](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/i18n/normalize-locale-path.ts#L8-L11), [packages/next/src/shared/lib/router/utils/app-paths.ts:23-52](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/app-paths.ts#L23-L52) ## Related - [[Server Request Lifecycle]] - [[Route Matching]] --- ## Technical docs: Route Matching URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/server-runtime/route-matching
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/base-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts) - [packages/next/src/server/config-shared.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts) - [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts) - [packages/next/src/server/route-matchers/pages-route-matcher.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matchers/pages-route-matcher.ts) - [packages/next-routing/src/resolve-routes.ts](https://github.com/blade47/next-routing/src/resolve-routes.ts) - [packages/next/src/server/api-utils/node/api-resolver.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts) - [packages/next/src/server/lib/router-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-server.ts) - [packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts) - [packages/next/src/server/route-matchers/app-page-route-matcher.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matchers/app-page-route-matcher.ts) - [packages/next/src/client/link.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/link.tsx) - [packages/next/errors.json](https://github.com/blade47/next.js/blob/main/packages/next/errors.json) - [packages/next/src/shared/lib/router/router.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/router.ts) - [packages/next/src/server/route-matcher-managers/route-matcher-manager.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-managers/route-matcher-manager.ts) - [packages/next/src/server/lib/router-utils/typegen.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/typegen.ts) - [packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts) - [packages/next/src/server/route-matchers/app-route-route-matcher.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matchers/app-route-route-matcher.ts) - [packages/next/src/server/route-matchers/route-matcher.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matchers/route-matcher.ts) - [packages/next/src/server/request-meta.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request-meta.ts) - [packages/next/src/server/route-modules/pages/pages-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/pages/pages-handler.ts) - [packages/next/src/server/route-matchers/pages-api-route-matcher.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matchers/pages-api-route-matcher.ts) - [packages/next/src/server/route-modules/app-page/helpers/prerender-manifest-matcher.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-page/helpers/prerender-manifest-matcher.ts) - [packages/next/src/server/lib/dev-bundler-service.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dev-bundler-service.ts) - [packages/next/src/server/route-matcher-providers/pages-route-matcher-provider.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/pages-route-matcher-provider.ts) - [packages/next/src/server/route-modules/app-page/module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-page/module.ts) - [packages/next/src/client/image-component.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/image-component.tsx) - [packages/next/src/server/route-matcher-providers/app-route-route-matcher-provider.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/app-route-route-matcher-provider.ts) - [packages/next/src/shared/lib/router/utils/interception-routes.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/interception-routes.ts) - [packages/next/src/server/route-matcher-providers/app-page-route-matcher-provider.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/app-page-route-matcher-provider.ts) - [packages/next/src/server/route-matchers/locale-route-matcher.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matchers/locale-route-matcher.ts) - [packages/next/src/client/components/segment-cache/optimistic-routes.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts)
## Overview ### Overview Route matching in Next.js is the foundational mechanism responsible for mapping incoming HTTP requests or client-side navigation paths to the correct App Router (`app/`) or Pages Router (`pages/`) execution handler. When a request hits the server or a client initiates a transition, Next.js must resolve file-system routes, dynamic slugs, internationalized locales, and API endpoints without ambiguity. The subsystem solves this through an extensible architecture composed of route matcher providers, individual route matchers, route pattern normalizers, and the `DefaultRouteMatcherManager`. Sources: [packages/next/src/server/base-server.ts:802-848](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L802-L848) The design separates manifest loading and route pattern transformation from the active lookup engine. Matchers are divided into static and dynamic collections, with dynamic routes sorted deterministically using specificity rules. Furthermore, client-side optimistic route prediction uses a trie-based structure to bypass server round-trips for known paths. This wiki page details the architecture, request pipelines, execution walkthroughs, and trade-offs of the Next.js route matching subsystem. Sources: [packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts:19-292](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts#L19-L292) --- ## Route Matcher Providers and Manifest Ingestion The route matching subsystem relies on route matcher providers to ingest build-time manifests (such as `pages-manifest.json` and `app-paths-manifest.json`) and transform them into concrete `RouteMatcher` instances. The base server configures a `DefaultRouteMatcherManager` by pushing provider instances through `BaseServer.getRouteMatchers()`. Sources: [packages/next/src/server/base-server.ts:802-848](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L802-L848) The supported provider classes and their corresponding manifests and route kinds include: | Provider Class | Target Manifest | Route Kind | Description | | :--- | :--- | :--- | :--- | | `PagesRouteMatcherProvider` | `pages-manifest.json` | `RouteKind.PAGES` | Matches non-API page components under `pages/` | | `PagesAPIRouteMatcherProvider` | `pages-manifest.json` | `RouteKind.PAGES_API` | Matches API route handlers under `pages/api/` | | `AppPageRouteMatcherProvider` | `app-paths-manifest.json` | `RouteKind.APP_PAGE` | Matches React Server Component pages under `app/` | | `AppRouteRouteMatcherProvider` | `app-paths-manifest.json` | `RouteKind.APP_ROUTE` | Matches App Router Route Handlers (`route.ts`) | Sources: [packages/next/src/server/route-matcher-providers/pages-route-matcher-provider.ts:16-82](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/pages-route-matcher-provider.ts#L16-L82), [packages/next/src/server/route-matcher-providers/app-page-route-matcher-provider.ts:13-60](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/app-page-route-matcher-provider.ts#L13-L60), [packages/next/src/server/route-matcher-providers/app-route-route-matcher-provider.ts:12-47](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/app-route-route-matcher-provider.ts#L12-L47) --- ## Core Matcher Hierarchy and Execution At the core of individual route matching is the `RouteMatcher` class, which wraps a `RouteDefinition` and optionally compiles a dynamic regular expression matcher if the pathname contains dynamic route segments. Specialized subclasses like `LocaleRouteMatcher`, `PagesRouteMatcher`, and `AppPageRouteMatcher` extend this behavior to handle internationalization or specific route metadata. Sources: [packages/next/src/server/route-matchers/route-matcher.ts:16-66](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matchers/route-matcher.ts#L16-L66) ```mermaid classDiagram class RouteMatcher { +D definition +Array~RouteMatcher~ duplicated +get identity() +get isDynamic() +match(pathname) +test(pathname) } class LocaleRouteMatcher { +get identity() +match(pathname, options) +test(pathname, options) } class PagesRouteMatcher { } class AppPageRouteMatcher { +get identity() } class AppRouteRouteMatcher { } RouteMatcher <|-- LocaleRouteMatcher RouteMatcher <|-- PagesRouteMatcher RouteMatcher <|-- AppPageRouteMatcher RouteMatcher <|-- AppRouteRouteMatcher LocaleRouteMatcher <|-- PagesLocaleRouteMatcher LocaleRouteMatcher <|-- PagesAPILocaleRouteMatcher ``` Sources: [packages/next/src/server/route-matchers/route-matcher.ts:16-66](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matchers/route-matcher.ts#L16-L66), [packages/next/src/server/route-matchers/locale-route-matcher.ts:15-85](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matchers/locale-route-matcher.ts#L15-L85) When `RouteMatcher.test(pathname)` is invoked, it checks whether the matcher is dynamic. If `this.dynamic` is defined, it executes the compiled `RouteMatchFn` to extract route parameters; otherwise, it performs an exact string comparison: ```typescript public test(pathname: string): RouteMatchResult | null { if (this.dynamic) { const params = this.dynamic(pathname) if (!params) return null return { params } } if (pathname === this.definition.pathname) { return {} } return null } ``` Sources: [packages/next/src/server/route-matchers/route-matcher.ts:52-65](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matchers/route-matcher.ts#L52-L65) --- ## RouteMatcherManager and Matching Order The `DefaultRouteMatcherManager` orchestrates multiple providers and manages collections of static and dynamic matchers. During initialization or reload, it sorts dynamic routes using `getSortedRoutes` to ensure that specific static segments take precedence over dynamic slugs and catch-all routes. Sources: [packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts:19-173](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts#L19-L173) ```mermaid flowchart TD A["Incoming Request Pathname"] --> B{"Is Dynamic Route?"} B -->|No| C["Iterate matchers.static"] C -->|Match Found| D["Yield Match"] C -->|No Match| E{"options.skipDynamic?"} B -->|Yes| F["Iterate matchers.dynamic (Sorted)"] E -->|True| G["Return null"] E -->|False| F F -->|Match Found| D F -->|No Match| G ``` Sources: [packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts:245-291](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts#L245-L291) > [!WARNING] > If a match is attempted before route compilation finishes (`this.lastCompilationID !== this.compilationID`), the manager throws an invariant error: `'Invariant: expected routes to have been loaded before match'`. This guards against requests racing route initialization. Sources: [packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts:255-259](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts#L255-L259) --- ## Call-Chain Execution Walkthrough When Next.js processes an incoming request inside `NextServer.handleRequest` (or the router server), route matching follows a precise, multi-step pipeline starting from URL normalization and locale analysis down to matcher evaluation and meta attachment. Sources: [packages/next/src/server/next-server.ts:1102-1120](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1102-L1120) The call chain proceeds as follows: 1. **Request Normalization (`BaseServer` / `AppPageRouteModule.normalizeUrl`)**: The incoming URL pathname has repeated slashes normalized, trailing slashes adjusted, and RSC or prefetch headers inspected (`RSCPathnameNormalizer`, `SegmentPrefixRSCPathnameNormalizer`). 2. **Locale Analysis (`I18NProvider`)**: If internationalization is configured, the request pathname is analyzed to detect and strip domain or path locales. 3. **Matcher Manager Invocation (`DefaultRouteMatcherManager.match`)**: The normalized pathname (with a guaranteed leading slash via `ensureLeadingSlash`) is passed to `matchers.match(pathname, options)`. Sources: [packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts:245-263](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts#L245-L263), [packages/next/src/server/route-modules/app-page/module.ts:116-153](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-page/module.ts#L116-L153) 4. **Static Validation (`validate`)**: `matchAll` first checks `this.matchers.static`. If `!isDynamicRoute(pathname)`, static matchers are tested instantly against the pathname. 5. **Dynamic Search and Sorting**: If no static match occurs, dynamic matchers in `this.matchers.dynamic` are evaluated in sorted order of specificity. 6. **Request Meta Attachment**: Once a `RouteMatch` object containing the `definition` and `params` is returned, it is attached to request metadata via `addRequestMeta(req, 'match', match)` to prevent redundant matching during subsequent rendering phases. Sources: [packages/next/src/server/next-server.ts:1117-1120](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1117-L1120), [packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts:264-287](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts#L264-L287) --- ## Client-Side Optimistic Route Matching (Segment Cache) In the App Router client, the segment cache implements optimistic routing via `optimistic-routes.ts`. This module predicts route structures for URLs that have not been prefetched yet by traversing a trie of `KnownRoutePart` nodes. Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:1-44](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L1-L44) ```mermaid erDiagram KnownRoutePart { Map staticChildren FulfilledRouteCacheEntry pattern KnownRoutePart dynamicChild string dynamicChildParamName string dynamicChildParamType } RouteTree ||--o{ KnownRoutePart : "discovers" FulfilledRouteCacheEntry ||--o{ KnownRoutePart : "serves as pattern" ``` Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:70-136](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L70-L136) When `matchKnownRoute(now, pathname, search)` is called, it splits the pathname into segments and invokes `matchKnownRoutePart`: - **Static Children Priority**: Exact static segment matches take precedence over dynamic children to prevent `/blog/featured` from incorrectly matching `/blog/[slug]`. - **Dynamic Matching**: Evaluates regular dynamic segments (`[param]`, type `'d'`), required catch-alls (`[...param]`, type `'c'`), and optional catch-alls (`[[...param]]`, type `'oc'`), accumulating parameter values in `resolvedParams`. - **Rewrite De-opt**: If a mismatch is detected due to a dynamic rewrite (`hasDynamicRewrite`), the pattern flags the entry and bails out to server resolution. Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:607-693](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L607-L693), [packages/next/src/client/components/segment-cache/optimistic-routes.ts:743-845](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L743-L845) --- ## Design Trade-Offs | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | **Manifest-Driven Providers** | Decouples route discovery from the request path; avoids filesystem IO during active requests. | Requires manifest synchronization during build or HMR compilation updates. | | **Separated Static and Dynamic Matcher Arrays** | Allows $O(1)$ or fast direct checks for non-dynamic pathnames, bypassing regex evaluation. | Requires sorting dynamic routes upon registration to preserve specificity order. | | **Client-Side Trie (Known Routes)** | Bypasses server round-trips for predicted route structures, improving transition performance. | Increases client memory usage (append-only trie) and requires de-opting on dynamic rewrites or interception routes. | Sources: [packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts:13-17](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts#L13-L17), [packages/next/src/client/components/segment-cache/optimistic-routes.ts:33-44](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L33-L44) --- ## Worked Example: Server-Side Route Matching Integration The following example demonstrates how `BaseServer` initializes route matchers and how `NextServer` consumes route matches to execute Pages API routes or render pages: ```typescript import { DefaultRouteMatcherManager } from './route-matcher-managers/default-route-matcher-manager' import { PagesRouteMatcherProvider } from './route-matcher-providers/pages-route-matcher-provider' import { ServerManifestLoader } from './route-matcher-providers/helpers/manifest-loaders/server-manifest-loader' import { PAGES_MANIFEST } from '../shared/lib/constants' // 1. Create a server manifest loader const manifestLoader = new ServerManifestLoader((name) => { if (name === PAGES_MANIFEST) { return { '/about': 'pages/about.js' } } return null }) // 2. Instantiate matcher manager and register provider const matchers = new DefaultRouteMatcherManager() matchers.push(new PagesRouteMatcherProvider('/dist', manifestLoader)) // 3. Wait for readiness and match a pathname await matchers.waitTillReady() const match = await matchers.match('/about', {}) if (match) { console.log(`Matched route definition:`, match.definition.page) // Output: Matched route definition: /about } ``` Sources: [packages/next/src/server/base-server.ts:802-848](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L802-L848), [packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts:37-53](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-managers/default-route-matcher-manager.ts#L37-L53) ## Related - [[Routing and Normalization]] - [[App Server Rendering]] --- ## Technical docs: Async Request Context URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/server-runtime/async-request-context
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/app-render/work-async-storage.external.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/work-async-storage.external.ts) - [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts) - [packages/next/src/server/async-storage/request-store.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/request-store.ts) - [packages/next/src/server/app-render/after-task-async-storage-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/after-task-async-storage-instance.ts) - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/server/request/headers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/headers.ts) - [packages/next/src/server/app-render/work-unit-async-storage.external.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/work-unit-async-storage.external.ts) - [packages/next/src/server/app-render/action-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts) - [packages/next/src/server/app-render/work-async-storage-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/work-async-storage-instance.ts) - [packages/next/src/server/app-render/work-unit-async-storage-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/work-unit-async-storage-instance.ts) - [packages/next/src/experimental/testmode/context.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testmode/context.ts) - [packages/next/src/server/request/cookies.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/cookies.ts) - [packages/next/src/server/after/after-context.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after-context.ts) - [packages/next/src/server/app-render/after-task-async-storage.external.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/after-task-async-storage.external.ts) - [packages/next/src/server/web/adapter.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts) - [packages/next/src/server/async-storage/with-store.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/with-store.ts) - [packages/next/src/server/after/after.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after.ts) - [packages/next/src/server/after/builtin-request-context.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/builtin-request-context.ts) - [packages/next/src/server/app-render/action-async-storage-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-async-storage-instance.ts) - [packages/next/src/server/async-storage/work-store.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/work-store.ts) - [packages/next/src/server/web/spec-extension/adapters/request-cookies.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts) - [packages/next/src/server/app-render/async-local-storage.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/async-local-storage.ts) - [packages/next/src/server/app-render/dynamic-access-async-storage-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-access-async-storage-instance.ts) - [packages/next/src/server/after/run-with-after.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/run-with-after.ts) - [packages/next/src/server/app-render/console-async-storage-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/console-async-storage-instance.ts) - [packages/next/src/client/components/segment-cache/navigation-testing-lock.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation-testing-lock.ts) - [packages/next/src/server/app-render/action-async-storage.external.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-async-storage.external.ts) - [packages/next/src/server/dev/use-cache-probe-worker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts) - [packages/next/src/server/app-render/dynamic-access-async-storage.external.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-access-async-storage.external.ts) - [packages/next/src/server/app-render/console-async-storage.external.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/console-async-storage.external.ts)
## Overview Async Request Context serves as the underlying execution tracking and scoping mechanism in Next.js, managing asynchronous data flow across server rendering passes, server actions, route handlers, and background tasks. By leveraging Node.js `AsyncLocalStorage` alongside custom store implementations, it solves the problem of safely propagating request headers, cookies, render phases, and cache configurations down deeply nested component trees without relying on global mutable state or explicit prop drilling. Key design decisions separate global work parameters from per-request metadata and cached execution scopes, preventing state leaks between independent cache boundaries and prerender passes. It interacts closely with dynamic APIs such as `headers()` and `cookies()` to enforce phase-based mutation rules, track dynamic data access, and schedule deferred background execution via `after()`. Sources: [packages/next/src/server/app-render/work-async-storage.external.ts:17-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/work-async-storage.external.ts#L17-L151), [packages/next/src/server/use-cache/use-cache-wrapper.ts:620-626](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L620-L626), [packages/next/src/server/async-storage/request-store.ts:99-123](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/request-store.ts#L99-L123), [packages/next/src/server/request/headers.ts:42-56](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/headers.ts#L42-L56), [packages/next/src/server/request/cookies.ts:35-50](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/cookies.ts#L35-L50), [packages/next/src/server/after/after-context.ts:39-53](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after-context.ts#L39-L53), [packages/next/src/server/web/adapter.ts:339-346](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L339-L346) ## AsyncLocalStorage Infrastructure and Store Hierarchy ### Overview Next.js builds its asynchronous context mechanism on top of an abstraction layer that wraps Node.js `AsyncLocalStorage`. When `AsyncLocalStorage` is unavailable in a runtime environment, the infrastructure falls back to a `FakeAsyncLocalStorage` implementation that throws errors on store operations like `run()`, `disable()`, `exit()`, and `enterWith()`. The base factory function `createAsyncLocalStorage()` inspects `globalThis.AsyncLocalStorage` to instantiate either the native instance or the fallback variant. Sources: [packages/next/src/server/app-render/async-local-storage.ts:7-46](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/async-local-storage.ts#L7-L46) ```typescript const maybeGlobalAsyncLocalStorage = typeof globalThis !== 'undefined' && (globalThis as any).AsyncLocalStorage export function createAsyncLocalStorage< Store extends {}, >(): AsyncLocalStorage { if (maybeGlobalAsyncLocalStorage) { return new maybeGlobalAsyncLocalStorage() } return new FakeAsyncLocalStorage() } ``` Sources: [packages/next/src/server/app-render/async-local-storage.ts:36-46](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/async-local-storage.ts#L36-L46) ### Store Hierarchy and Definitions The application rendering engine divides execution contexts across distinct store types. Each store instance wraps `AsyncLocalStorage` with a specialized interface defining its contextual properties. | Store Instance Module | Interface Type | Store Properties / Shape | | :--- | :--- | :--- | | `action-async-storage-instance.ts` | `ActionAsyncStorage` | `readonly isAction?: boolean`, `readonly isAppRoute?: boolean` | | `console-async-storage-instance.ts` | `ConsoleAsyncStorage` | `readonly dim: boolean` | | `dynamic-access-async-storage-instance.ts` | `DynamicAccessStorage` | `readonly abortController: AbortController` | | `work-async-storage-instance.ts` | `WorkAsyncStorage` | *Defined via external storage module* | | `work-unit-async-storage-instance.ts` | `WorkUnitAsyncStorage` | *Defined via external storage module* | | `after-task-async-storage-instance.ts` | `AfterTaskAsyncStorage` | *Defined via external storage module* | Sources: [packages/next/src/server/app-render/action-async-storage.external.ts:5-10](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-async-storage.external.ts#L5-L10), [packages/next/src/server/app-render/console-async-storage.external.ts:6-15](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/console-async-storage.external.ts#L6-L15), [packages/next/src/server/app-render/dynamic-access-async-storage.external.ts:6-10](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-access-async-storage.external.ts#L6-L10), [packages/next/src/server/app-render/action-async-storage-instance.ts:4-6](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-async-storage-instance.ts#L4-L6), [packages/next/src/server/app-render/console-async-storage-instance.ts:4-6](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/console-async-storage-instance.ts#L4-L6), [packages/next/src/server/app-render/dynamic-access-async-storage-instance.ts:4-6](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-access-async-storage-instance.ts#L4-L6) > [!NOTE] > `ConsoleStore` utilizes the `dim` property to control output coloring. When `dim` is set to true, log colors are dimmed to indicate that the log originates from a repeat or validation render that is irrelevant to the primary server action. Sources: [packages/next/src/server/app-render/console-async-storage.external.ts:6-13](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/console-async-storage.external.ts#L6-L13) ### Snapshot Binding and Store Wrapping To propagate execution contexts across asynchronous boundaries, helper utilities manage function binding and snapshot creation. `bindSnapshot` delegates to native `AsyncLocalStorage.bind()` or `FakeAsyncLocalStorage.bind()`, while `createSnapshot()` captures execution state or returns the identity function when native capabilities are absent. Sources: [packages/next/src/server/app-render/async-local-storage.ts:48-68](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/async-local-storage.ts#L48-L68) ```typescript export function bindSnapshot( fn: T ): T { if (maybeGlobalAsyncLocalStorage) { return maybeGlobalAsyncLocalStorage.bind(fn) } return FakeAsyncLocalStorage.bind(fn) } export function createSnapshot(): ( fn: (...args: TArgs) => R, ...args: TArgs ) => R { if (maybeGlobalAsyncLocalStorage) { return maybeGlobalAsyncLocalStorage.snapshot() } return function (fn: any, ...args: any[]) { return fn(...args) } } ``` Sources: [packages/next/src/server/app-render/async-local-storage.ts:48-68](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/async-local-storage.ts#L48-L68) Additionally, the generic `WithStore` type signature standardizes how storage implementations supply a context to callback functions: ```typescript export type WithStore = ( storage: AsyncLocalStorage, context: Context, callback: (store: Store) => Result ) => Result ``` Sources: [packages/next/src/server/async-storage/with-store.ts:12-16](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/with-store.ts#L12-L16) ## RequestStore and HTTP Context Lifecycle ### Overview The request store lifecycle centers on initializing per-request state, sanitizing incoming headers, binding request cookies, and bridging runtime adapters between Node.js request/response pairs and Edge handlers or probe workers. The `RequestStore` instance relies on decoupled inputs, allowing contexts to be instantiated without requiring a live Node.js `IncomingMessage` or `BaseNextRequest`. Sources: [packages/next/src/server/async-storage/request-store.ts:92-98](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/request-store.ts#L92-L98) ### Request Store Initialization and Headers Cleaning When a request store is created for rendering via `createRequestStoreForRender`, it assigns a default phase of `'render'`, extracts headers from `req.headers`, and configures cookie update callbacks. The internal `getHeaders` helper processes raw headers using `HeadersAdapter.from(headers)` and strips internal plumbing headers such as `FLIGHT_HEADERS`, `NEXT_REQUEST_ID_HEADER`, and `NEXT_HTML_REQUEST_ID_HEADER` before sealing the headers instance. Sources: [packages/next/src/server/async-storage/request-store.ts:33-49](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/request-store.ts#L33-L49), [packages/next/src/server/async-storage/request-store.ts:158-174](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/request-store.ts#L158-L174) ```typescript function getHeaders(headers: Headers | IncomingHttpHeaders): ReadonlyHeaders { const cleaned = HeadersAdapter.from(headers) for (const header of FLIGHT_HEADERS) { cleaned.delete(header) } cleaned.delete(NEXT_REQUEST_ID_HEADER) cleaned.delete(NEXT_HTML_REQUEST_ID_HEADER) return HeadersAdapter.seal(cleaned) } ``` Sources: [packages/next/src/server/async-storage/request-store.ts:33-49](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/request-store.ts#L33-L49) ### Middleware Cookie Merging If middleware sets cookies on a request via the `x-middleware-set-cookie` header, `mergeMiddlewareCookies` parses the cookie string using `splitCookiesString`, wraps them in a `ResponseCookies` container, and merges them into the existing request cookies object so that subsequent `cookies()` calls can access newly written cookies. Sources: [packages/next/src/server/async-storage/request-store.ts:130-156](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/request-store.ts#L130-L156) ```typescript function mergeMiddlewareCookies( headers: Headers | IncomingHttpHeaders, existingCookies: RequestCookies | ResponseCookies ) { if ( 'x-middleware-set-cookie' in headers && typeof headers['x-middleware-set-cookie'] === 'string' ) { const setCookieValue = headers['x-middleware-set-cookie'] const responseHeaders = new Headers() for (const cookie of splitCookiesString(setCookieValue)) { responseHeaders.append('set-cookie', cookie) } const responseCookies = new ResponseCookies(responseHeaders) for (const cookie of responseCookies.getAll()) { existingCookies.set(cookie) } } } ``` Sources: [packages/next/src/server/async-storage/request-store.ts:130-156](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/request-store.ts#L130-L156) ### Runtime Adapter Bridges and Worker Probes In Edge runtimes and middleware adapters, execution bridges bind request stores using `createRequestStoreForAPI`. For example, `packages/next/src/server/web/adapter.ts` constructs implicit tags, wraps cookie updates, and runs the request and work async storage scopes around the middleware handler: Sources: [packages/next/src/server/web/adapter.ts:281-346](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L281-L346) ```typescript const requestStore = createRequestStoreForAPI( request, request.nextUrl, implicitTags, onUpdateCookies, previewProps ) return await workAsyncStorage.run(workStore, () => workUnitAsyncStorage.run( requestStore, params.handler, request, event ) ) ``` Sources: [packages/next/src/server/web/adapter.ts:294-346](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L294-L346) Similarly, the cache probe worker (`use-cache-probe-worker.ts`) initializes a throwaway request store from a serializable request snapshot without an underlying Node.js socket, enabling isolated re-executions for `'use cache'` deadlock detection: Sources: [packages/next/src/server/dev/use-cache-probe-worker.ts:155-167](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts#L155-L167) ```typescript const workUnitStore = createRequestStore({ phase: 'render', headers: new Headers(msg.request.headers), onUpdateCookies: undefined, url: { pathname: msg.request.urlPathname, search: msg.request.urlSearch }, rootParams: msg.request.rootParams, implicitTags: { tags: [], expirationsByCacheKind: new Map() }, resumeDataCache: null, previewProps: undefined, isHmrRefresh: msg.request.isHmrRefresh, serverComponentsHmrCache: undefined, fallbackParams: null, }) ``` Sources: [packages/next/src/server/dev/use-cache-probe-worker.ts:155-167](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts#L155-L167) | Request Store Input Property | Expected Type / Shape | Purpose in Request Lifecycle | | :--- | :--- | :--- | | `phase` | `RequestStore['phase']` | Defines the current execution phase (e.g., `'render'`) | | `headers` | `Headers \| IncomingHttpHeaders` | Raw request headers before adapter sanitization | | `onUpdateCookies` | `((cookies: string[]) => void) \| undefined` | Callback invoked when userspace mutates cookies | | `url` | `{ pathname: string; search?: string }` | Identifies the pathname and search components of the request URL | | `rootParams` | `Params` | Root route parameters for the active render | | `implicitTags` | `ImplicitTags` | Cache tags implicitly associated with the request route | | `resumeDataCache` | `ResumeDataCache \| null` | Cache used for streaming resume data during rendering | | `previewProps` | `__ApiPreviewProps \| undefined` | Configuration properties for draft and preview modes | | `isHmrRefresh` | `boolean \| undefined` | Flag indicating whether the request stems from a Hot Module Replacement refresh | | `serverComponentsHmrCache` | `ServerComponentsHmrCache \| undefined` | Cache storage for Server Components during development HMR | | `fallbackParams` | `OpaqueFallbackRouteParams \| null \| undefined` | Opaque fallback parameters for static paths | Sources: [packages/next/src/server/async-storage/request-store.ts:99-123](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/request-store.ts#L99-L123) ## WorkStore and WorkUnitStore Architecture ### Overview The Next.js rendering engine relies on a dual-store architecture managed via Node.js `AsyncLocalStorage` instances: `WorkStore` (via `workAsyncStorage`) and `WorkUnitStore` (via `workUnitAsyncStorage`). While `WorkStore` tracks top-level request and build configuration metadata across the entire render tree, `WorkUnitStore` encapsulates specific execution scopes such as incoming requests, cache boundaries (`"use cache"` or `unstable_cache`), prerenders, and static generation parameter sweeps. This separation ensures that request-specific state cannot leak into cached scopes. Sources: [packages/next/src/server/app-render/work-async-storage.external.ts:1-156](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/work-async-storage.external.ts#L1-L156), [packages/next/src/server/app-render/work-unit-async-storage.external.ts:369-426](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/work-unit-async-storage.external.ts#L369-L426) ### WorkStore Field Definitions and Static Generation Rules `WorkStore` is initialized through `createWorkStore` and tracks global options, build identifiers, timeout configurations, and deduplication maps for fetch metrics and cache invocations. Sources: [packages/next/src/server/app-render/work-async-storage.external.ts:17-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/work-async-storage.external.ts#L17-L151), [packages/next/src/server/async-storage/work-store.ts:83-162](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/work-store.ts#L83-L162) | WorkStore Property | Type | Purpose / Description | | :--- | :--- | :--- | | `isStaticGeneration` | `boolean` | Determines if the current pass is a static prerender. | | `page` | `string` | File path relative to the page being rendered. | | `route` | `string` | Normalized route path without trailing `/page` or `/route`. | | `incrementalCache` | `IncrementalCache \| undefined` | Global or local incremental cache instance. | | `useCacheTimeout` | `number` | Timeout limit in milliseconds for `"use cache"` executions. | | `staticPageGenerationTimeout` | `number` | Timeout limit for static page generation. | | `pendingCacheInvocations` | `Map>` | Intra-request deduplication map keyed by coarse cache key. | | `runInCleanSnapshot` | `((fn: (...args: TArgs) => R, ...args: TArgs) => R)` | Executes functions inside a clean `AsyncLocalStorage` snapshot. | Sources: [packages/next/src/server/app-render/work-async-storage.external.ts:17-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/work-async-storage.external.ts#L17-L151), [packages/next/src/server/async-storage/work-store.ts:83-162](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/work-store.ts#L83-L162) The determination of static generation follows strict rules based on render options: Sources: [packages/next/src/server/async-storage/work-store.ts:109-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/work-store.ts#L109-L114) ```typescript const isStaticGeneration = !renderOpts.shouldWaitOnAllReady && !renderOpts.supportsDynamicResponse && !renderOpts.isDraftMode && !renderOpts.isPossibleServerAction ``` Sources: [packages/next/src/server/async-storage/work-store.ts:109-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/async-storage/work-store.ts#L109-L114) ### WorkUnitStore Variants and Cache Shadowing `WorkUnitStore` is a union type representing different execution units. When entering a cache scope, `createUseCacheStore` constructs a `UseCacheStore` that shadows any outer request store, explicitly preventing the leakage of request-specific objects like unmasked cookies or headers while selectively copying required properties. Sources: [packages/next/src/server/app-render/work-unit-async-storage.external.ts:372-422](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/work-unit-async-storage.external.ts#L372-L422), [packages/next/src/server/use-cache/use-cache-wrapper.ts:640-720](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L640-L720) | WorkUnitStore Type | Identifier String | Scope & Behavior | | :--- | :--- | :--- | | `RequestStore` | `'request'` | Represents an active HTTP request with cookies, headers, and resume cache. | | `PublicUseCacheStore` | `'cache'` | Public `"use cache"` boundary isolating requests from cached output. | | `PrivateUseCacheStore` | `'private-cache'` | Private cache boundary retaining scoped headers, cookies, and root parameters. | | `UnstableCacheStore` | `'unstable-cache'` | Legacy `unstable_cache` scope where root parameters are `undefined`. | | `PrerenderStore` | `'prerender'`, `'prerender-ppr'`, etc. | Manages static prerendering, streaming, and staged rendering controllers. | | `GenerateStaticParamsStore` | `'generate-static-params'` | Tracks generation parameters (`rootParams`) during static path generation. | Sources: [packages/next/src/server/app-render/work-unit-async-storage.external.ts:372-422](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/work-unit-async-storage.external.ts#L372-L422), [packages/next/src/server/use-cache/use-cache-wrapper.ts:640-720](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L640-L720) > [!WARNING] > Inside an `UnstableCacheStore`, `rootParams` is always hardcoded as `undefined`. Any nested `"use cache"` function attempting to access route parameters in this context will encounter `undefined` and throw an error. Sources: [packages/next/src/server/app-render/work-unit-async-storage.external.ts:391-399](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/work-unit-async-storage.external.ts#L391-L399) ### Cache Generation Walkthrough and Clean Snapshots When generating a cache entry, Next.js detaches from request-specific contexts by executing through a series of wrappers that clear and restore the storage layers: Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts:585-638](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L585-L638) `generateCacheEntry()` calls `workStore.runInCleanSnapshot()`, which invokes `generateCacheEntryWithRestoredWorkStore()`. This function resets the asynchronous context and binds the work store via `workAsyncStorage.run()`, before passing execution to `generateCacheEntryWithCacheContext()`: Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts:585-638](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L585-L638) ```typescript function generateCacheEntry( workStore: WorkStore, cacheContext: CacheContext, clientReferenceManifest: DeepReadonly, encodedArguments: FormData | string, fn: (...args: unknown[]) => Promise, timeoutError: UseCacheTimeoutError, deadlockError: UseCacheDeadlockError | undefined ) { return workStore.runInCleanSnapshot( generateCacheEntryWithRestoredWorkStore, workStore, cacheContext, clientReferenceManifest, encodedArguments, fn, timeoutError, deadlockError ) } ``` Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts:585-609](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L585-L609) > [!NOTE] > Request stores and prerender stores are explicitly excluded from cache generation snapshots. This guarantees that request-scoped elements such as `cookies()` inside a `React.cache()` invocation cannot leak into or contaminate cached outputs. Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts:620-626](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L620-L626) ## Headers and Cookies Access Adapters ### Overview The `headers()` and `cookies()` functions provide asynchronous access to incoming HTTP request headers and request-response cookie stores. These APIs integrate with asynchronous storage to enforce dynamic tracking, validate execution phases, and prevent synchronous access or improper usage across cache scopes and background callbacks. Sources: [packages/next/src/server/request/headers.ts:33-56](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/headers.ts#L33-L56), [packages/next/src/server/request/cookies.ts:35-50](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/cookies.ts#L35-L50) ### Phase-Based Cookie Mutability Enforcement Cookies can only be modified when the request store is operating within specific lifecycle phases, such as during a Server Action. The `areCookiesMutableInCurrentPhase` function inspects the `requestStore.phase` property to determine whether mutation is permitted. Sources: [packages/next/src/server/web/spec-extension/adapters/request-cookies.ts:208-210](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts#L208-L210) ```typescript export function areCookiesMutableInCurrentPhase(requestStore: RequestStore) { return requestStore.phase === 'action' } ``` Sources: [packages/next/src/server/web/spec-extension/adapters/request-cookies.ts:208-210](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts#L208-L210) When mutation methods like `set` or `delete` are invoked on `cookies()`, `createCookiesWithMutableAccessCheck` wraps the target store and triggers `ensureCookiesAreStillMutable()`. If the current phase has transitioned (such as moving from `action` to `render` or `render` to `after`), mutation attempts throw a `ReadonlyRequestCookiesError`. Sources: [packages/next/src/server/web/spec-extension/adapters/request-cookies.ts:181-227](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts#L181-L227) > [!WARNING] > Attempting to modify cookies via `cookies().set()` or `cookies().delete()` outside of a Server Action or Route Handler phase triggers `ReadonlyRequestCookiesError`, halting execution with an unmodifiable cookies error. Sources: [packages/next/src/server/web/spec-extension/adapters/request-cookies.ts:12-22](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts#L12-L22) ### Dynamic Tracking and Ergonomic Protections Both `headers()` and `cookies()` return promises that resolve to read-only or mutable collections. To discourage synchronous access anti-patterns (such as calling properties directly on the returned promise), Next.js instruments the promise objects with warning descriptors in development mode. Sources: [packages/next/src/server/request/headers.ts:250-271](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/headers.ts#L250-L271), [packages/next/src/server/request/cookies.ts:259-278](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/cookies.ts#L259-L278) | Instrumenting Function | Target Promise Type | Properties / Symbols Intercepted | | :--- | :--- | :--- | | `instrumentHeadersPromiseWithDevWarnings` | `Promise` | `Symbol.iterator`, `append`, `delete`, `get`, `has`, `set`, `getSetCookie`, `forEach`, `keys`, `values`, `entries` | | `instrumentCookiesPromiseWithDevWarnings` | `Promise` | `Symbol.iterator`, `size`, `get`, `getAll`, `has`, `set`, `delete`, `clear`, `toString` | Sources: [packages/next/src/server/request/headers.ts:250-271](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/headers.ts#L250-L271), [packages/next/src/server/request/cookies.ts:259-278](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/cookies.ts#L259-L278) > [!NOTE] > Synchronously accessing methods or properties on the unresolved `headers()` or `cookies()` promise in development invokes `createHeadersAccessError` or `createCookiesAccessError`, reminding developers to unwrap the promise using `await` or `React.use()`. Sources: [packages/next/src/server/request/headers.ts:317-327](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/headers.ts#L317-L327), [packages/next/src/server/request/cookies.ts:324-334](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/cookies.ts#L324-L334) ## After-Response Tasks and Execution Context ### Overview The `after()` API allows developers to schedule callbacks and promises to execute after the current request finishes processing. Managing this deferred work relies on the `AfterContext` class, `AfterRunner`, and the `afterTaskAsyncStorage` instance to preserve request execution contexts and manage task error handling across background boundaries. Sources: [packages/next/src/server/after/after.ts:6-21](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after.ts#L6-L21), [packages/next/src/server/after/after-context.ts:21-37](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after-context.ts#L21-L37), [packages/next/src/server/after/run-with-after.ts:12-33](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/run-with-after.ts#L12-L33) ### Scheduling and Execution Flow When an `after()` task is submitted, execution flows through validation and queue management steps. The following call chain illustrates how a task moves from invocation to execution: Sources: [packages/next/src/server/after/after.ts:9-20](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after.ts#L9-L20), [packages/next/src/server/after/after-context.ts:39-102](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after-context.ts#L39-L102) `after()` → `workAsyncStorage.getStore()` → `afterContext.after()` → `addCallback()` → `bindSnapshot()` → `afterTaskAsyncStorage.run()` → `callbackQueue.add()` Sources: [packages/next/src/server/after/after.ts:9-20](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after.ts#L9-L20), [packages/next/src/server/after/after-context.ts:39-102](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after-context.ts#L39-L102) > [!WARNING] > Calling `after()` outside of a request scope throws an error (`\`after\` was called outside a request scope`), as it requires an active `workStore` containing an initialized `afterContext`. Sources: [packages/next/src/server/after/after.ts:10-17](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after.ts#L10-L17) ### `AfterContext` Options and Properties The behavior and lifecycle of deferred tasks are governed by configuration options passed into `AfterContext`. Sources: [packages/next/src/server/after/after-context.ts:15-37](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after-context.ts#L15-L37) | Option / Property | Type | Description | | :--- | :--- | :--- | | `waitUntil` | `RequestLifecycleOpts['waitUntil'] \| undefined` | Platform function to extend the lifetime of the request worker for promises and callback execution. | | `onClose` | `RequestLifecycleOpts['onClose']` | Handler triggered when the request connection closes, initiating callback execution. | | `onTaskError` | `RequestLifecycleOpts['onAfterTaskError'] \| undefined` | Custom error handler invoked if a background task or promise rejects. | | `callbackQueue` | `PromiseQueue` | Internal queue managing callback execution order and concurrency. | | `workUnitStores` | `Set` | Set tracking associated work unit stores whose phases transition to `'after'` during execution. | Sources: [packages/next/src/server/after/after-context.ts:15-37](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after-context.ts#L15-L37) ### Task Error Handling and Runner Integration The `AfterRunner` class orchestrates request lifecycle closure and error boundaries using `AwaiterOnce`, `CloseController`, and a detached promise tracker. Sources: [packages/next/src/server/after/run-with-after.ts:12-33](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/run-with-after.ts#L12-L33) ```typescript export class AfterRunner { private awaiter = new AwaiterOnce() private closeController = new CloseController() private finishedWithoutErrors = new DetachedPromise() readonly context: Ctx = { waitUntil: this.awaiter.waitUntil.bind(this.awaiter), onClose: this.closeController.onClose.bind(this.closeController), onTaskError: (error) => this.finishedWithoutErrors.reject(error), } public async executeAfter() { this.closeController.dispatchClose() await this.awaiter.awaiting() this.finishedWithoutErrors.resolve() return this.finishedWithoutErrors.promise } } ``` Sources: [packages/next/src/server/after/run-with-after.ts:12-33](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/run-with-after.ts#L12-L33) > [!NOTE] > When a callback or promise passed to `after()` throws or rejects, `reportTaskError` catches the error, logs it via `console.error`, and triggers `onTaskError` if defined, wrapping any handler failures in an `InvariantError`. Sources: [packages/next/src/server/after/after-context.ts:127-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/after-context.ts#L127-L151) ## Related - [[Server Request Lifecycle]] - [[Staged Dynamic Rendering]] --- ## Technical docs: Stream Handling URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/server-runtime/stream-handling
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/server/pipe-readable.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/pipe-readable.ts) - [packages/next/src/server/app-render/stream-ops.node.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.node.ts) - [packages/next/src/server/render-result.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts) - [packages/next/src/server/stream-utils/node-web-streams-helper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/stream-utils/node-web-streams-helper.ts) - [packages/next/src/server/send-response.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/send-response.ts) - [packages/next/src/server/app-render/action-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts) - [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts) - [packages/next/src/client/components/segment-cache/cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts) - [packages/next/src/server/app-render/use-flight-response.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/use-flight-response.tsx) - [packages/next/src/server/app-render/stream-ops.web.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.web.ts) - [packages/next/src/server/app-render/app-render-prerender-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render-prerender-utils.ts) - [packages/next/src/server/body-streams.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/body-streams.ts) - [packages/next/src/server/base-http/node.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts) - [packages/next/src/server/app-render/instant-validation/stream-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/stream-utils.ts) - [packages/next/src/server/send-payload.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/send-payload.ts) - [packages/next/src/server/app-render/stream-ops.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.ts) - [packages/next/src/server/dev/debug-channel.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/debug-channel.ts) - [packages/next/src/server/dev/hot-reloader-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts) - [packages/next/src/server/dev/hot-reloader-webpack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts)
## Overview Stream handling in Next.js bridges the gap between heterogeneous rendering environments—supporting Node.js `Readable`/`PassThrough` streams, standard Web `ReadableStream` instances, and incoming HTTP body streams. During server rendering and response generation, streaming architectures allow server components (RSC) and React Fizz SSR pipelines to emit chunks incrementally to clients, reducing time-to-first-byte (TTFB) and enabling partial prerendering (PPR) or streaming HTML hydration. Sources: [packages/next/src/server/app-render/stream-ops.ts:1-9](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.ts#L1-L9) Because Next.js runs across different runtimes (Node.js vs. Edge) and bundlers (Webpack vs. Turbopack), stream handling employs compile-time switches, conditional adapters, and buffered transform streams. These mechanisms guarantee backpressure management, safe stream teeing and replaying during prerendering, and clean pipe propagation to Node.js `ServerResponse` objects. Sources: [packages/next/src/server/app-render/stream-ops.node.ts:1-10](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.node.ts#L1-L10), [packages/next/src/server/pipe-readable.ts:124-147](https://github.com/blade47/next.js/blob/main/packages/next/src/server/pipe-readable.ts#L124-L147) --- ## Stream Abstraction and Compile-Time Switching Next.js unifies Node.js streams and Web streams via conditional compilation and shared interfaces. The core stream operations router (`stream-ops.ts`) dynamically resolves to either `stream-ops.node.ts` or `stream-ops.web.ts` depending on whether `process.env.__NEXT_USE_NODE_STREAMS` is active and whether the target is the Edge runtime. Sources: [packages/next/src/server/app-render/stream-ops.ts:27-31](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.ts#L27-L31) Both underlying modules export an identical type surface defined as `AnyStream`: ```typescript export type AnyStream = ReadableStream | Readable ``` Sources: [packages/next/src/server/app-render/app-render-prerender-utils.ts:4](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render-prerender-utils.ts#L4-L4) This structural identity allows stream operators (such as `continueFizzStream`, `chainStreams`, and `streamToBuffer`) to accept either stream type without runtime casting. When running under Node.js with native stream optimizations enabled, Web Streams produced by React are converted to Node.js `Readable` instances using `Readable.fromWeb()`, allowing Next.js to leverage Node's high-performance pipeline primitives and backpressure management. Sources: [packages/next/src/server/stream-utils/node-web-streams-helper.ts:134-157](https://github.com/blade47/next.js/blob/main/packages/next/src/server/stream-utils/node-web-streams-helper.ts#L134-L157) | Module File | Target Runtime / Condition | Primary Implementation | | :--- | :--- | :--- | | `stream-ops.ts` | Compile-time dispatcher | Selects Node or Web implementation based on environment flags | | `stream-ops.node.ts` | Node.js with `__NEXT_USE_NODE_STREAMS = true` | Uses Node.js `PassThrough`, `Transform`, and `Readable.fromWeb()` | | `stream-ops.web.ts` | Edge runtime or Web Streams mode | Uses standard `ReadableStream`, `TransformStream`, and `.tee()` | Sources: [packages/next/src/server/app-render/stream-ops.ts:27-31](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.ts#L27-L31), [packages/next/src/server/app-render/stream-ops.node.ts:1-17](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.node.ts#L1-L17), [packages/next/src/server/app-render/stream-ops.web.ts:1-29](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.web.ts#L1-L29) --- ## Response Piping and Backpressure Mechanics When delivering rendered output to an HTTP client via `RenderResult.pipeToNodeResponse(res)` or `pipeToNodeResponse()`, Next.js bridges `ReadableStream` or Node `Readable` instances directly to Node.js `ServerResponse` streams. Sources: [packages/next/src/server/render-result.ts:405-417](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts#L405-L417) For Web `ReadableStream` inputs, `pipe-readable.ts` creates a custom WritableStream adapter (`createWriterFromResponse`) that manages header flushing, OTEL performance metrics, and backpressure: ```typescript export async function pipeToNodeResponse( readable: ReadableStream, res: ServerResponse, waitUntilForEnd?: Promise ) { try { const { errored, destroyed } = res if (errored || destroyed) return const controller = createAbortController(res) const writer = createWriterFromResponse(res, waitUntilForEnd) await readable.pipeTo(writer, { signal: controller.signal }) } catch (err: any) { if (isAbortError(err)) return throw new Error('failed to pipe response', { cause: err }) } } ``` Sources: [packages/next/src/server/pipe-readable.ts:124-147](https://github.com/blade47/next.js/blob/main/packages/next/src/server/pipe-readable.ts#L124-L147) > [!IMPORTANT] > Headers are deliberately not flushed until the first chunk is written (`if (!started) { started = true; res.flushHeaders(); }`). This ensures that status codes, cookies, and headers can still be modified during early stream rendering stages before bytes hit the socket. Sources: [packages/next/src/server/pipe-readable.ts:50-80](https://github.com/blade47/next.js/blob/main/packages/next/src/server/pipe-readable.ts#L50-L80) Backpressure is governed by the return value of `res.write(chunk)`. If `res.write()` returns `false`, indicating that the underlying kernel buffer is saturated, the writer awaits the response `drain` event before continuing the pipe operation: ```typescript const ok = res.write(chunk) if (!ok) { await drained.promise drained = new DetachedPromise() } ``` Sources: [packages/next/src/server/pipe-readable.ts:83-98](https://github.com/blade47/next.js/blob/main/packages/next/src/server/pipe-readable.ts#L83-L98) --- ## Prerendering and Replayable Streams During static generation or Partial Prerendering (PPR), Next.js must frequently split or re-use a single React Server Components (RSC) render stream across multiple consumers (e.g., generating shell markup, inlined data scripts, and static payloads). Because standard `ReadableStream.tee()` or Node streams cannot be consumed multiple times without specialized handling, Next.js implements `ReplayableNodeStream` and `ReactServerResult`. Sources: [packages/next/src/server/app-render/app-render-prerender-utils.ts:14-99](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render-prerender-utils.ts#L14-L99) ```mermaid flowchart TD A["Node.js Readable Source"] --> B["ReplayableNodeStream"] B --> C["Buffered Chunks Array"] B --> D["Subscribers Set"] C --> E["createReplayStream() #1"] C --> F["createReplayStream() #2"] D --> G["Live Chunks (on 'data')"] E --> H["Consumer A (Fizz SSR)"] F --> I["Consumer B (Inlined Data)"] ``` Sources: [packages/next/src/server/app-render/app-render-prerender-utils.ts:99-221](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render-prerender-utils.ts#L99-L221) `ReplayableNodeStream` buffers incoming chunks into memory while simultaneously notifying active subscribers. When `createReplayStream()` is called, it constructs a new Node.js `Readable` that replays all previously buffered chunks via pull-based `_read()` execution before forwarding live chunks as they arrive: ```typescript const stream = new ReadableCtor({ read() { if (!bufferDrained) { bufferDrained = true for (let i = bufferIndex; i < bufferedChunks.length; i++) { this.push(bufferedChunks[i]) } bufferIndex = bufferedChunks.length if (isDone) { this.push(null) } } }, }) ``` Sources: [packages/next/src/server/app-render/app-render-prerender-utils.ts:179-192](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render-prerender-utils.ts#L179-L192) > [!NOTE] > Buffered chunks are delivered via pull-based `_read()` rather than pushed eagerly. This prevents asynchronous task scheduling from capturing an empty `AsyncLocalStorage` context when `createReplayStream()` is invoked outside request scopes. Sources: [packages/next/src/server/app-render/app-render-prerender-utils.ts:140-149](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render-prerender-utils.ts#L140-L149) --- ## Request Body Streams and Cloneable Bodies Incoming HTTP request bodies (`IncomingMessage`) are processed as streams during API route execution, middleware processing, and Server Action payload decoding. Because a Node.js request stream can only be read once, `getCloneableBody()` provides body duplication capabilities with strict memory limits. Sources: [packages/next/src/server/body-streams.ts:43-58](https://github.com/blade47/next.js/blob/main/packages/next/src/server/body-streams.ts#L43-L58) ```typescript export function getCloneableBody( readable: T, sizeLimit?: number ): CloneableBody { let buffered: Readable | null = null // ... return { cloneBodyStream() { const input = buffered ?? readable const p1 = new PassThrough() const p2 = new PassThrough() let bytesRead = 0 const bodySizeLimit = sizeLimit ?? DEFAULT_BODY_CLONE_SIZE_LIMIT let limitExceeded = false input.on('data', (chunk) => { if (limitExceeded) return bytesRead += chunk.length if (bytesRead > bodySizeLimit) { limitExceeded = true p1.push(null) p2.push(null) return } p1.push(chunk) p2.push(chunk) }) // ... buffered = p2 return p1 } } } ``` Sources: [packages/next/src/server/body-streams.ts:47-115](https://github.com/blade47/next.js/blob/main/packages/next/src/server/body-streams.ts#L47-L115) When `cloneBodyStream()` is invoked, it taps into the input stream via dual `PassThrough` streams (`p1` and `p2`), accumulating a buffered copy (`p2`) while streaming data to the caller (`p1`). If the accumulated payload exceeds `DEFAULT_BODY_CLONE_SIZE_LIMIT` (10MB), a warning is logged and both streams are terminated early to prevent unbounded memory consumption. Sources: [packages/next/src/server/body-streams.ts:6-6](https://github.com/blade47/next.js/blob/main/packages/next/src/server/body-streams.ts#L6-L6), [packages/next/src/server/body-streams.ts:79-116](https://github.com/blade47/next.js/blob/main/packages/next/src/server/body-streams.ts#L79-L116) --- ## Inlined Data Streams and Server Action Decoding For RSC responses rendered during App Router navigation, Flight data chunks must be injected into the HTML document as self-executing script tags (`self.__next_f.push(...)`). This is handled by `createNodeInlinedDataStream` and `createInlinedDataReadableStream`. Sources: [packages/next/src/server/app-render/stream-ops.node.ts:908-935](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.node.ts#L908-L935), [packages/next/src/server/app-render/use-flight-response.tsx:165-220](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/use-flight-response.tsx#L165-L220) The stream reads binary or text chunks from the RSC flight stream, serializes them into JSON instructions, and wraps them in HTML script tags with proper escaping (`htmlEscapeJsonString`). Binary chunks that cannot be decoded as valid UTF-8 strings are automatically converted to Base64 data instructions: ```typescript const base64 = Buffer.from( chunk.buffer, chunk.byteOffset, chunk.byteLength ).toString('base64') htmlInlinedData = htmlEscapeJsonString( JSON.stringify([INLINE_FLIGHT_PAYLOAD_BINARY, base64]) ) ``` Sources: [packages/next/src/server/app-render/use-flight-response.tsx:256-267](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/use-flight-response.tsx#L256-L267) Conversely, when decoding Server Actions (`action-handler.ts`), incoming multipart form data or JSON payloads are validated against `bodySizeLimitBytes` (defaulting to 1MB) using a size-limiting transform stream before being passed to `decodeReply` or `busboy`: ```typescript const sizeLimitTransform = new Transform({ transform(chunk, encoding, callback) { size += Buffer.byteLength(chunk, encoding) if (size > bodySizeLimitBytes) { callback(new ApiError(413, `Body exceeded ${bodySizeLimit} limit.`)) return } callback(null, chunk) }, }) ``` Sources: [packages/next/src/server/app-render/action-handler.ts:901-931](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L901-L931) --- ## Development Debug Channels and Buffering In development mode, Next.js streams React debug channel events and HMR payloads to the browser over WebSockets. To prevent excessive network overhead from small, frequent stream pushes, chunks are batched using `createBufferedTransformStream` (Web Streams) or `createNodeBufferedTransformStream` (Node streams) with a 128KB buffer threshold (`MAX_DEBUG_CHANNEL_BATCH_BYTES`). Sources: [packages/next/src/server/dev/debug-channel.ts:10-12](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/debug-channel.ts#L10-L12), [packages/next/src/server/dev/debug-channel.ts:68-93](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/debug-channel.ts#L68-L93) ```typescript export function createBufferedTransformStream( options: BufferedTransformOptions = {} ): TransformStream { const { maxBufferByteLength = Infinity } = options let bufferedChunks: Array = [] let bufferByteLength: number = 0 let pending: DetachedPromise | undefined const flush = (controller: TransformStreamDefaultController) => { if (bufferedChunks.length === 0) return const chunk = new Uint8Array(bufferByteLength) let copiedBytes = 0 for (let i = 0; i < bufferedChunks.length; i++) { chunk.set(bufferedChunks[i], copiedBytes) copiedBytes += bufferedChunks[i].byteLength } bufferedChunks.length = 0 bufferByteLength = 0 controller.enqueue(chunk) } // ... } ``` Sources: [packages/next/src/server/stream-utils/node-web-streams-helper.ts:227-261](https://github.com/blade47/next.js/blob/main/packages/next/src/server/stream-utils/node-web-streams-helper.ts#L227-L261) The transformation buffers incoming chunks until `bufferByteLength >= maxBufferByteLength`, or schedules an immediate flush via microtask (`scheduleImmediate`) if the buffer is below the limit, ensuring low latency while batching small packets. Sources: [packages/next/src/server/stream-utils/node-web-streams-helper.ts:263-292](https://github.com/blade47/next.js/blob/main/packages/next/src/server/stream-utils/node-web-streams-helper.ts#L263-L292) When an HMR event or HTML request debug channel is established, `connectReactDebugChannel` resolves the stream type, applies this buffered transform stream, and pushes data chunks to the browser. Sources: [packages/next/src/server/dev/debug-channel.ts:28-97](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/debug-channel.ts#L28-L97), [packages/next/src/server/dev/hot-reloader-turbopack.ts:1278-1282](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts#L1278-L1282) --- ## Call-Chain Execution Walkthrough: Response Pipe Operation The following sequence traces how a rendered output (`RenderResult`) is piped to an HTTP client response object: Sources: [packages/next/src/server/render-result.ts:405-417](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts#L405-L417) 1. **`RenderResult.pipeToNodeResponse(res)`** checks if the response payload is a Node.js `Readable` stream. If so, it delegates to `pipeNodeReadableToNodeResponse()`; otherwise, it extracts `.readable` (a Web `ReadableStream`) and calls `pipeToNodeResponse()`. Sources: [packages/next/src/server/render-result.ts:405-417](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts#L405-L417) 2. **`pipeToNodeResponse()`** creates an `AbortController` bound to the underlying `ServerResponse` close/error events via `createAbortController(res)`, initializes `createWriterFromResponse()`, and executes `readable.pipeTo(writer, { signal })`. Sources: [packages/next/src/server/pipe-readable.ts:124-147](https://github.com/blade47/next.js/blob/main/packages/next/src/server/pipe-readable.ts#L124-L147) 3. **`createWriterFromResponse.write(chunk)`** intercepts the first emitted chunk, flushes response headers (`res.flushHeaders()`), records OTEL client component metrics, and calls `res.write(chunk)`. Sources: [packages/next/src/server/pipe-readable.ts:20-84](https://github.com/blade47/next.js/blob/main/packages/next/src/server/pipe-readable.ts#L20-L84) 4. **Backpressure Check:** If `res.write(chunk)` returns `false`, the writer awaits `drained.promise` (which resolves on the Node `drain` event) before proceeding. Sources: [packages/next/src/server/pipe-readable.ts:83-98](https://github.com/blade47/next.js/blob/main/packages/next/src/server/pipe-readable.ts#L83-L98) 5. **Completion:** Upon stream completion, `close()` awaits any pending `waitUntil` background promises before calling `res.end()` and resolving the finish promise. Sources: [packages/next/src/server/pipe-readable.ts:109-121](https://github.com/blade47/next.js/blob/main/packages/next/src/server/pipe-readable.ts#L109-L121) ```mermaid sequenceDiagram participant RR as RenderResult participant PNR as pipeToNodeResponse participant WR as createWriterFromResponse participant SR as ServerResponse RR->>PNR: pipeToNodeResponse(readable, res, waitUntil) PNR->>WR: createWriterFromResponse(res, waitUntil) PNR->>SR: readable.pipeTo(writer, { signal }) loop For each chunk SR->>WR: write(chunk) alt First chunk WR->>SR: flushHeaders() end WR->>SR: res.write(chunk) alt Backpressure (false) SR-->>WR: drain event end end SR-->>WR: close / finish event WR->>SR: await waitUntil, res.end() ``` Sources: [packages/next/src/server/render-result.ts:405-417](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts#L405-L417), [packages/next/src/server/pipe-readable.ts:20-147](https://github.com/blade47/next.js/blob/main/packages/next/src/server/pipe-readable.ts#L20-L147) --- ## Stream Operations Design Trade-Offs | Design Choice | Benefit | Cost / Trade-off | | :--- | :--- | :--- | | **Compile-Time Stream Switcher** (`stream-ops.ts`) | Eliminates runtime overhead and dead-code branches in edge vs. node bundles | Requires maintaining dual module implementations (`stream-ops.node.ts` & `stream-ops.web.ts`) with identical type surfaces | | **Pull-Based Replay Buffering** (`ReplayableNodeStream`) | Prevents eager chunk emission from capturing empty `AsyncLocalStorage` contexts | Requires buffering stream chunks in memory until consumers read them | | **Dual PassThrough Body Cloning** (`getCloneableBody`) | Allows simultaneous consumption by Middleware and route handlers | Doubles memory allocation for request bodies up to the size limit | | **Buffered Debug Transformation** (`createBufferedTransformStream`) | Reduces WebSocket message frequency and serialization overhead by batching up to 128KB | Introduces minor buffering latency for debug chunk delivery | Sources: [packages/next/src/server/app-render/stream-ops.ts:27-31](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.ts#L27-L31), [packages/next/src/server/app-render/app-render-prerender-utils.ts:99-149](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render-prerender-utils.ts#L99-L149), [packages/next/src/server/body-streams.ts:79-116](https://github.com/blade47/next.js/blob/main/packages/next/src/server/body-streams.ts#L79-L116), [packages/next/src/server/dev/debug-channel.ts:10-12](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/debug-channel.ts#L10-L12) ## Related - [[App Server Rendering]] - [[Staged Dynamic Rendering]] --- ## Technical docs: App Server Rendering URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-router-rendering/app-server-rendering
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx) - [packages/next/src/client/app-index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx) - [packages/next/src/server/base-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts) - [packages/next/src/server/render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx) - [packages/next/src/server/app-render/instant-validation/instant-validation.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx) - [packages/next/src/server/app-render/collect-segment-data.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/collect-segment-data.tsx) - [packages/next/src/server/app-render/create-component-tree.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/create-component-tree.tsx) - [packages/next/src/server/app-render/entry-base.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/entry-base.ts) - [packages/next/src/server/client-component-renderer-logger.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/client-component-renderer-logger.ts) - [packages/next/src/server/app-render/action-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts) - [packages/next/src/shared/lib/router/utils/parse-loader-tree.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/parse-loader-tree.ts)
## Overview App Server Rendering coordinates the server-side generation, request processing, and React Server Component (RSC) streaming lifecycle for Next.js App Router applications. It bridges base server request reception and route dispatch with nested component tree assembly, hierarchical router state traversal, and specialized execution pipelines like Server Actions and instant validation. By orchestrating payload serialization and segment data collection across static, runtime, and dynamic rendering phases, the system delivers precise incremental updates and hydrated client states. Sources: [packages/next/src/server/app-render/app-render.tsx:2471-2485](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L2471-L2485), [packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx:28-56](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx#L28-L56), [packages/next/src/client/app-index.tsx:349-388](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L349-L388), [packages/next/src/server/app-render/action-handler.ts:538-558](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L538-L558) ## Server Request Routing and Dispatch ### Overview Request routing and dispatch bridge incoming HTTP requests from `BaseServer` into the application render pipeline defined in `app-render.tsx`. When a request arrives, `BaseServer` matches route patterns utilizing route matcher providers such as `AppPageRouteMatcherProvider`, `AppRouteRouteMatcherProvider`, and pages manifest loaders [packages/next/src/server/base-server.ts:98-103]. Once matched, dispatch mechanisms initialize metadata, parse request headers, configure work and request stores, and invoke `renderToHTMLOrFlightImpl` to produce either rendered HTML or serialized Flight RSC payloads [packages/next/src/server/app-render/app-render.tsx:2471-2485]. Sources: [packages/next/src/server/base-server.ts:98-103](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L98-L103), [packages/next/src/server/app-render/app-render.tsx:2471-2485](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L2471-L2485) ### Request Dispatch Call Chain The execution sequence from initial request reception to stream generation flows through specific core functions: 1. `BaseServer` matches incoming URL paths using provider managers and loads route component modules. 2. `renderToHTMLOrFlightImpl` receives `req`, `res`, `url`, `pagePath`, `query`, and `renderOpts`, setting initial status codes (such as `404` for `/404` paths) and unique request timestamps [packages/next/src/server/app-render/app-render.tsx:2471-2488, 2495]. 3. `ComponentMod.patchFetch()` is executed, and module loaders extract `loaderTree` properties from `routeModule.userland` [packages/next/src/server/app-render/app-render.tsx:2571-2577]. 4. Request header flags (`flightRouterState`, `isPrefetchRequest`, `isRSCRequest`, `isHmrRefresh`) are extracted to determine request classification [packages/next/src/server/app-render/app-render.tsx:2599-2607]. 5. Unique identifiers (`requestId` and `htmlRequestId`) are generated or parsed from headers via crypto hashing or `nanoid` [packages/next/src/server/app-render/app-render.tsx:2609-2632]. 6. Implicit tags are resolved using `getImplicitTags` with `resolvedPathname`, and an `AppRenderContext` (`ctx`) object is constructed [packages/next/src/server/app-render/app-render.tsx:2644-2678]. 7. `workStore.isStaticGeneration` evaluates whether to trigger `prerenderToStream` or direct dynamic rendering [packages/next/src/server/app-render/app-render.tsx:2594, 2682-2694]. Sources: [packages/next/src/server/app-render/app-render.tsx:2471-2694](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L2471-L2694) > [!NOTE] > Request IDs are derived using different strategies based on execution context: cryptographic SHA-1 hashes of request URLs during static generation, `crypto.randomUUID()` in Edge runtimes, and `nanoid()` in Node.js runtimes. Sources: [packages/next/src/server/app-render/app-render.tsx:2613-2624](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L2613-L2624) ### Route Matcher Providers Reference | Provider Class | Source File | Purpose | | :--- | :--- | :--- | | `AppPageRouteMatcherProvider` | `packages/next/src/server/route-matcher-providers/app-page-route-matcher-provider.ts` | Discovers and matches App Router page segments (`page.js/tsx`) | | `AppRouteRouteMatcherProvider` | `packages/next/src/server/route-matcher-providers/app-route-route-matcher-provider.ts` | Discovers and matches App Router API route handlers (`route.js/ts`) | | `PagesAPIRouteMatcherProvider` | `packages/next/src/server/route-matcher-providers/pages-api-route-matcher-provider.ts` | Discovers and matches legacy Pages API routes | | `PagesRouteMatcherProvider` | `packages/next/src/server/route-matcher-providers/pages-route-matcher-provider.ts` | Discovers and matches legacy Pages router pages | Sources: [packages/next/src/server/base-server.ts:99-102](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L99-L102) ## Component Tree and LoaderTree Construction ### Overview Next.js parses hierarchical loader trees (`LoaderTree`) to construct the nested React Server Component tree and cache node seed data. The loader tree utility `parseLoaderTree` extracts route segments, parallel routes, and module conventions from the tree array structure, identifying the active segment key and resolving convention paths for layouts, templates, and pages. Sources: [packages/next/src/shared/lib/router/utils/parse-loader-tree.ts:4-23](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/parse-loader-tree.ts#L4-L23) ### Loader Tree Parsing and Parameter Resolution The loader tree structure tuple contains four primary elements: `[segment, parallelRoutes, modules, staticSiblings]`. The helper function `parseLoaderTree` extracts these fields, checking if the segment matches `DEFAULT_SEGMENT_KEY` (`__DEFAULT__`) to fall back to `modules.defaultPage`. The active convention path is resolved by checking `layout?.[1] || template?.[1] || page?.[1]`. Sources: [packages/next/src/shared/lib/router/utils/parse-loader-tree.ts:4-23](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/parse-loader-tree.ts#L4-L23) Root and segment parameters are computed via recursive helper implementations that traverse down parallel route children until the root layout is reached. ```typescript export function getRootParams( loaderTree: LoaderTree, getDynamicParamFromSegment: GetDynamicParamFromSegment ): Params { return getRootParamsImpl({}, loaderTree, getDynamicParamFromSegment) } ``` Sources: [packages/next/src/server/app-render/create-component-tree.tsx:1198-1203](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/create-component-tree.tsx#L1198-L1203) ### Seed Data and Boundary Construction Component trees are wrapped and serialized into `CacheNodeSeedData` tuples via `createSeedData`. If segments are not runtime-prefetchable, rendering is deferred by awaiting the first late render stage (`FIRST_LATE_RENDER_STAGE`). When loading boundaries are present, `LoadingBoundaryProvider` wraps the React node element. ```typescript function createSeedData( ctx: AppRenderContext, rsc: React.ReactNode, parallelRoutes: Record, loading: LoadingModuleData | null, isPossiblyPartialResponse: boolean, isRuntimePrefetchable: boolean, varyParamsAccumulator: VaryParamsAccumulator | null ): CacheNodeSeedData { const createElement = ctx.componentMod.createElement if (!isRuntimePrefetchable) { const workUnitStore = workUnitAsyncStorage.getStore() if (workUnitStore) { let stagedRendering: StagedRenderingController | null | undefined switch (workUnitStore.type) { case 'request': case 'prerender-runtime': stagedRendering = workUnitStore.stagedRendering if (stagedRendering) { const deferredRsc = rsc rsc = stagedRendering .waitForStage(FIRST_LATE_RENDER_STAGE) .then(() => deferredRsc) } break case 'prerender': case 'prerender-client': case 'validation-client': case 'prerender-ppr': case 'prerender-legacy': case 'cache': case 'private-cache': case 'unstable-cache': case 'generate-static-params': break default: workUnitStore satisfies never } } } if (loading !== null) { const LoadingBoundaryProvider = ctx.componentMod.LoadingBoundaryProvider rsc = createElement(LoadingBoundaryProvider, { loading: loading, children: rsc, }) } return [ rsc, parallelRoutes, null, isPossiblyPartialResponse, varyParamsAccumulator ? getVaryParamsThenable(varyParamsAccumulator) : null, ] } ``` Sources: [packages/next/src/server/app-render/create-component-tree.tsx:1300-1367](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/create-component-tree.tsx#L1300-L1367) > [!NOTE] > `createSeedData` returns a tuple structure `[rsc, parallelRoutes, null, isPossiblyPartialResponse, varyParamsAccumulatorThenable]` which forms the base seed data consumed by client-side router caches during navigation and prefetching. Sources: [packages/next/src/server/app-render/create-component-tree.tsx:1360-1366](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/create-component-tree.tsx#L1360-L1366) ## Flight Router State Tree Traversal ### Overview The `walkTreeWithFlightRouterState` function traverses both the server-side `loaderTreeToFilter` and the client-side `flightRouterState` simultaneously. This dual-tree walk determines at what common layout or split point differential rendering must begin for client navigations or prefetches. Sources: [packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx:28-56](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx#L28-L56) ### Traversal and Rendering Decision Flow The traversal inspects each node of the loader tree, matching segments and evaluating router markers to decide whether to skip component rendering, emit metadata only, or trigger component tree generation at the current level. ```typescript const renderComponentsOnThisLevel = !flightRouterState || !matchSegment(actualSegment, flightRouterState[0]) || flightRouterState[3] === 'refetch' ``` Sources: [packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx:107-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx#L107-L114) ### Flight Router State Control Flags When client-side navigation or prefetching requests specific subsets of the tree, `flightRouterState[3]` carries control strings that modify traversal behavior. | Flag / Condition | Action / Behavior | Sources | | :--- | :--- | :--- | | `refetch` | Forces rendering components at this level regardless of segment match | [packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx:112-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx#L112-L114) | | `inside-shared-layout` | Marks the traversal as operating inside the shared layout boundary | [packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx:133-134](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx#L133-L134) | | `metadata-only` | Short-circuits traversal to return only page metadata and router state without segment data | [packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx:199-237](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx#L199-L237) | | Route Tree Prefetch | Invokes `createRouteTreePrefetch` to serialize structural hints without full component evaluation | [packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx:160-172](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx#L160-L172) | Sources: [packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx:112-237](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx#L112-L237) > [!WARNING] > If PPR is disabled and a prefetch request encounters no `loading` component in the tree, traversal short-circuits immediately and returns only the router state, avoiding expensive sub-tree renders. Sources: [packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx:135-145](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/walk-tree-with-flight-router-state.tsx#L135-L145) ## Server Action Pipeline and Execution ### Overview Server actions enable client-initiated mutations executed on the server. The `handleAction` function in `action-handler.ts` manages incoming server action invocations, request forwarding, payload decoding, revalidations, and state synchronization across workers. Sources: [packages/next/src/server/app-render/action-handler.ts:538-594](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L538-L594) ### Action Execution Call Chain When an action request is identified, execution flows through a precise sequence of steps from request validation to action resolution and post-execution cleanup: `handleAction()` → `getServerActionMetadata()` → `selectWorkerForForwarding()` → `actionAsyncStorage.run()` → `decodeReply()` / `decodeReplyFromBusboy()` → `executeActionAndPrepareForRender()` → `synchronizeMutableCookies()` → `executeRevalidates()` Sources: [packages/next/src/server/app-render/action-handler.ts:563-1354](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L563-L1354) ### Request Validation and CSRF Protection Before executing an action, `handleAction` verifies the request origin against the host header to prevent Cross-Site Request Forgery (CSRF). If an action ID belongs to a different worker thread, the request is forwarded via `createForwardedActionResponse`. ```typescript if (!originHost) { warning = 'Missing `origin` header from a forwarded Server Actions request.' } else if (!host || originHost !== host.value) { if (isCsrfOriginAllowed(originHost, serverActions?.allowedOrigins)) { // Ignore it } else { const error = new Error('Invalid Server Actions request.') throw error } } ``` Sources: [packages/next/src/server/app-render/action-handler.ts:646-707](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L646-L707) > [!CAUTION] > If a server action is invoked during static rendering (`workStore.isStaticGeneration`), `handleAction` immediately throws an invariant error because mutations are disallowed at build time. Sources: [packages/next/src/server/app-render/action-handler.ts:614-618](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L614-L618) ### State Synchronization and Revalidation Pipeline Upon successful completion of an action handler, `executeActionAndPrepareForRender` transitions the request store phase from `'action'` to `'render'`, synchronizes mutable cookies, and processes pending revalidations. | Action Phase / Function | Target State / Operation | Sources | | :--- | :--- | :--- | | `requestStore.phase` | Switches from `'action'` to `'render'` after action execution | [packages/next/src/server/app-render/action-handler.ts:1312-1335](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1312-L1335) | | `synchronizeMutableCookies` | Updates immutable cookies in the store with mutations performed during the action | [packages/next/src/server/app-render/action-handler.ts:1337-1342](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1337-L1342) | | `workStore.isDraftMode` | Reflects draft mode status from `requestStore.draftMode.isEnabled` | [packages/next/src/server/app-render/action-handler.ts:1344-1347](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1344-L1347) | | `executeRevalidates` | Awaits and applies all pending path and tag revalidations | [packages/next/src/server/app-render/action-handler.ts:1349-1352](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1349-L1352) | Sources: [packages/next/src/server/app-render/action-handler.ts:1300-1354](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1300-L1354) > [!NOTE] > Revalidation headers like `x-action-revalidated` are populated on the response via `addRevalidationHeader` depending on whether tags, cookies, or paths were invalidated. Sources: [packages/next/src/server/app-render/action-handler.ts:146-200](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L146-L200) ## Segment Data Collection and Validation ### Overview Segment data extraction and instant validation parse route payloads and manage prefetch segment streams. The subsystem relies on utilities like `traverseRootSeedDataSegments` to recursively walk router states, coordinate seed data extraction, and process individual route nodes during prefetches. Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:109-127](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L109-L127) ### Validation Planning and Segment Traversal The server plans validation workflows by converting segments and route trees into serializable paths. Traversal starts at the root payload, passing each segment path and seed data structure down through child routes via parallel route keys. ```typescript function traverseCacheNodeSegments( path: SegmentPath, route: FlightRouterState, seedData: CacheNodeSeedData, processSegment: ( segmentPath: SegmentPath, seedData: CacheNodeSeedData ) => void ): void { processSegment(path, seedData) const [_segment, childRoutes] = route const [_node, parallelRoutesData, _loading, _isPartial] = seedData for (const parallelRouteKey in childRoutes) { const childSeedData = parallelRoutesData[parallelRouteKey] if (!childSeedData) { throw new InvariantError( `Got unexpected empty seed data during instant validation` ) } const childRoute = childRoutes[parallelRouteKey] const [childSegment] = childRoute const childPath = createChildSegmentPath( path, parallelRouteKey, childSegment ) traverseCacheNodeSegments( childPath, childRoute, childSeedData, processSegment ) } } ``` Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:129-168](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L129-L168) > [!CAUTION] > If a parallel route key in `childRoutes` lacks corresponding `parallelRoutesData` within the cache node seed data, `traverseCacheNodeSegments` throws an `InvariantError` indicating unexpected empty seed data during instant validation. Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:145-150](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L145-L150) ### Prefetch Data Structures and Response Formatting Prefetch generation relies on structured definitions for root tree prefetches, segment parameters, and response payloads. The `SegmentPrefetchResponse` format packs a build ID alongside an array of nullable segment prefetches representing terminal segments and inlined ancestors. | Type Name | Key Fields | Purpose | Sources | | :--- | :--- | :--- | :--- | | `RootTreePrefetch` | `buildId`, `tree`, `staleTime` | Top-level metadata structure required before fetching segment data | [packages/next/src/server/app-render/collect-segment-data.tsx:44-48](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/collect-segment-data.tsx#L44-L48) | | `TreePrefetchParam` | `type`, `key`, `siblings` | Describes dynamic parameter constraints and static sibling segments | [packages/next/src/server/app-render/collect-segment-data.tsx:50-63](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/collect-segment-data.tsx#L50-L63) | | `SegmentPrefetchResponse` | `buildId`, `data` | Top-level response holding terminal and inlined ancestor segment data | [packages/next/src/server/app-render/collect-segment-data.tsx:91-94](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/collect-segment-data.tsx#L91-L94) | | `SegmentPrefetch` | `rsc`, `isPartial`, `staleTime`, `varyParams` | Carries rendered React nodes, partial status, and parameter dependency sets | [packages/next/src/server/app-render/collect-segment-data.tsx:96-110](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/collect-segment-data.tsx#L96-L110) | Sources: [packages/next/src/server/app-render/collect-segment-data.tsx:44-110](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/collect-segment-data.tsx#L44-L110) ## RSC Stream Emission and Serialization ### Overview The client-side hydration lifecycle bridges the server-emitted Flight stream with the browser DOM via specialized segment callbacks and stream registry writers. When the browser initializes app routing, `self.__next_f` captures inlined flight segments and decodes them into a React-readable stream. ```typescript function nextServerDataCallback(seg: FlightSegment): void { if (seg[0] === 0) { initialServerDataBuffer = [] } else if (seg[0] === 1) { if (!initialServerDataBuffer) throw new Error('Unexpected server data: missing bootstrap script.') if (initialServerDataWriter) { initialServerDataWriter.enqueue(encoder.encode(seg[1])) } else { initialServerDataBuffer.push(seg[1]) } } else if (seg[0] === 2) { initialFormStateData = seg[1] } else if (seg[0] === 3) { if (!initialServerDataBuffer) throw new Error('Unexpected server data: missing bootstrap script.') const binaryString = atob(seg[1]) const decodedChunk = new Uint8Array(binaryString.length) for (var i = 0; i < binaryString.length; i++) { decodedChunk[i] = binaryString.charCodeAt(i) } if (initialServerDataWriter) { initialServerDataWriter.enqueue(decodedChunk) } else { initialServerDataBuffer.push(decodedChunk) } } } ``` Sources: [packages/next/src/client/app-index.tsx:79-110](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L79-L110) > [!WARNING] > If a segment with type `1` or `3` arrives before a bootstrap segment of type `0` has initialized `initialServerDataBuffer`, `nextServerDataCallback` throws an error stating that the server data is missing the bootstrap script. Sources: [packages/next/src/client/app-index.tsx:83-85](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L83-L85), [packages/next/src/client/app-index.tsx:94-96](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L94-L96) ### Flight Segment Types and Payload Encoding Flight segments emitted into `self.__next_f` use prefix tags to differentiate bootstrap markers, text chunks, form state payloads, and base64-encoded binary chunks. | Segment Code | Tuple Shape | Purpose | Sources | | :--- | :--- | :--- | :--- | | `0` | `[isBootStrap: 0]` | Initializes the server data buffer array | [packages/next/src/client/app-index.tsx:59](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L59) | | `1` | `[isNotBootstrap: 1, responsePartial: string]` | Enqueues or buffers text partial responses | [packages/next/src/client/app-index.tsx:60](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L60) | | `2` | `[isFormState: 2, formState: any]` | Captures initial form state data | [packages/next/src/client/app-index.tsx:61](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L61) | | `3` | `[isBinary: 3, responseBase64Partial: string]` | Decodes and buffers base64 binary chunks | [packages/next/src/client/app-index.tsx:62](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L62) | Sources: [packages/next/src/client/app-index.tsx:58-63](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L58-L63) ### Hydration and Stream Coordination Client initialization coordinates reader streams, client component loading instrumentation, and DOM content loaded states to complete React hydration. ```typescript export async function hydrate( instrumentationHooks: ClientInstrumentationHooks | null, assetPrefix: string ) { let staticIndicatorState: StaticIndicatorState | undefined let webSocket: WebSocket | undefined if (process.env.__NEXT_DEV_SERVER) { const { createWebSocket } = require('./dev/hot-reloader/app/web-socket') as typeof import('./dev/hot-reloader/app/web-socket') staticIndicatorState = { pathname: null, appIsrManifest: null } webSocket = createWebSocket(assetPrefix, staticIndicatorState) } const initialRSCPayload = await initialServerResponse if (process.env.__NEXT_USE_OFFLINE) { require('./components/offline') as typeof import('./components/offline') } if (initialRSCPayload.b) { setNavigationBuildId(initialRSCPayload.b!) } else { setNavigationBuildId(getDeploymentId()!) } const initialTimestamp = Date.now() const actionQueue: AppRouterActionQueue = createMutableActionQueue( createInitialRouterState({ navigatedAt: initialTimestamp, initialRSCPayload, initialFlightStreamForCache, location: window.location, }), instrumentationHooks ) const reactEl = ( ) if (document.documentElement.id === '__next_error__') { let element = reactEl if (process.env.NODE_ENV !== 'production') { const { RootLevelDevOverlayElement } = require('../next-devtools/userspace/app/client-entry') as typeof import('../next-devtools/userspace/app/client-entry') element = ( {element} ) } ReactDOMClient.createRoot(appElement, reactRootOptions).render(element) } else { React.startTransition(() => { ReactDOMClient.hydrateRoot(appElement, reactEl, { ...reactRootOptions, formState: initialFormStateData, }) }) } if (process.env.__NEXT_DEV_SERVER) { const { linkGc } = require('./app-link-gc') as typeof import('./app-link-gc') linkGc() } } ``` Sources: [packages/next/src/client/app-index.tsx:349-434](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L349-L434) ## Related - [[Staged Dynamic Rendering]] - [[Client App Router]] - [[Server Actions]] --- ## Technical docs: Staged Dynamic Rendering URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-router-rendering/staged-dynamic-rendering
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/server/app-render/dynamic-rendering.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts) - [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts) - [packages/next/src/server/base-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts) - [packages/next/src/server/route-modules/app-route/module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts) - [packages/next/src/server/app-render/staged-rendering.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts) - [packages/next/src/server/request/io.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/io.ts) - [packages/next/src/server/app-render/postponed-state.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/postponed-state.ts) - [packages/next/src/server/route-modules/pages/pages-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/pages/pages-handler.ts) - [packages/next/src/server/node-environment-extensions/io-utils.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/node-environment-extensions/io-utils.tsx) - [packages/next/src/server/dynamic-rendering-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dynamic-rendering-utils.ts) - [packages/next/src/server/app-render/sync-io-messages.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/sync-io-messages.ts) - [packages/next/src/server/app-render/vary-params.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/vary-params.ts) - [packages/next/src/client/components/navigation.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts) - [packages/next/src/server/request/params.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts)
## Overview Staged Dynamic Rendering is an advanced rendering architecture in Next.js that structures server-side rendering into discrete lifecycle stages—ranging from early static shell generation through runtime phases to fully dynamic rendering—controlled by the `StagedRenderingController`. This model allows Next.js to isolate static components and render cacheable data without deopting an entire React tree, replacing coarse deopts with fine-grained stage progression. By coordinating triggers, tracking dynamic data access, and orchestrating delayed parameter resolutions, the system prevents unnecessary blocking while managing cache interactions and serializing Flight streams effectively. Sources: [packages/next/src/server/app-render/app-render.tsx:944-952](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L944-L952), [packages/next/src/server/app-render/staged-rendering.ts:4-20](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L4-L20), [packages/next/src/server/app-render/dynamic-rendering.ts:6-10](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L6-L10) ## Staged Rendering Architecture and Controller ### Overview The staged rendering lifecycle divides server rendering into sequential, controllable execution steps managed by the `StagedRenderingController` class in `staged-rendering.ts`. Progression through these stages controls when specific data kinds—such as session data, static link data, and runtime link data—become available to the React rendering tree. The controller maintains an internal map of triggers for every advanceable render stage, resolving pending promises as the render moves forward. Sources: [packages/next/src/server/app-render/staged-rendering.ts:4-20](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L4-L20), [packages/next/src/server/app-render/staged-rendering.ts:84-106](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L84-L106) ### Render Stages and Progression Order The progression is governed by `RENDER_STAGE_ADVANCE_ORDER`, which sequences static and runtime phases into distinct shell, early, and standard milestones before reaching the final dynamic stage. | Render Stage | Enum Value | Category / Meaning | | :--- | :--- | :--- | | `RenderStage.Before` | `1` | Initial state prior to render execution. | | `RenderStage.ShellEarlyStatic` | `10` | Early static shell phase. | | `RenderStage.ShellStatic` | `11` | Late static shell phase (`FIRST_LATE_RENDER_STAGE`). | | `RenderStage.EarlyStatic` | `12` | Early static data phase. | | `RenderStage.Static` | `13` | Standard static rendering phase. | | `RenderStage.ShellEarlyRuntime` | `20` | Early runtime session shell phase. | | `RenderStage.ShellRuntime` | `21` | Late runtime session shell phase. | | `RenderStage.EarlyRuntime` | `22` | Early runtime data phase. | | `RenderStage.Runtime` | `23` | Standard runtime rendering phase. | | `RenderStage.Dynamic` | `30` | Fully dynamic fallback rendering stage. | | `RenderStage.Abandoned` | `40` | Render aborted or abandoned state. | Sources: [packages/next/src/server/app-render/staged-rendering.ts:4-41](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L4-L41) ### Controller Trigger Coordination and Lifecycle Execution The `StagedRenderingController` constructor initializes stage triggers and wires up event listeners for abort signals and abandon controllers. When `advanceStage(targetStage)` is called, the controller verifies that the target does not exceed `finalStage` and checks whether the target is ahead of `currentStage`. Sources: [packages/next/src/server/app-render/staged-rendering.ts:108-148](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L108-L148), [packages/next/src/server/app-render/staged-rendering.ts:314-325](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L314-L325) The stage advancement flow executes through the following call chain: `StagedRenderingController.advanceStage()` → determines index range via `RENDER_STAGE_ADVANCE_ORDER.indexOf()` → iterates over intermediate stages calling `this.resolveStage()` → `fireStageTrigger()` → executes registered listeners in `trigger._listeners` and invokes `trigger._resolvePromise()`. ```typescript // Example instantiation and stage progression sequence in node rendering const stageController = new StagedRenderingController({ abortSignal: null, abandonController: null, shouldTrackSyncIO: false, finalStage: null, }) // Advance through static stages during node flight stream generation stageController.advanceStage(RenderStage.ShellStatic) stageController.advanceStage(RenderStage.Static) stageController.advanceStage(RenderStage.Dynamic) ``` Sources: [packages/next/src/server/app-render/app-render.tsx:944-952](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L944-L952), [packages/next/src/server/app-render/app-render.tsx:1035-1073](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L1035-L1073), [packages/next/src/server/app-render/staged-rendering.ts:314-362](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L314-L362), [packages/next/src/server/app-render/staged-rendering.ts:440-466](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L440-L466) > [!NOTE] > `cancelStageTrigger` suppresses unhandled rejection warnings automatically by attaching a no-op catch handler to `trigger.promise` when an abort signal rejects pending stage triggers. Sources: [packages/next/src/server/app-render/staged-rendering.ts:124-137](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L124-L137), [packages/next/src/server/app-render/staged-rendering.ts:469-482](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L469-L482) ## Dynamic Data Access and Postpone Handling ### Overview Dynamic data access and postponement handling govern how Next.js tracks runtime variables, deopts component trees, and coordinates client-side navigation hooks. When code reads dynamic properties or navigation parameters during prerendering, Next.js captures these occurrences via explicit tracking structures, triggers React postponements, or aborts static generation depending on the active render unit and configuration flags. Sources: [packages/next/src/server/app-render/dynamic-rendering.ts:1-21](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L1-L21), [packages/next/src/server/app-render/dynamic-rendering.ts:167-236](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L167-L236) ### Dynamic Tracking State and Access Annotation The `DynamicTrackingState` structure manages properties recorded during Server Component rendering. It maintains `isDebugDynamicAccesses`, an array of `dynamicAccesses` storing individual `DynamicAccess` objects containing an optional stack trace and the accessed expression string, alongside sync error handling flags `syncDynamicErrorWithStack` and `syncDynamicErrorWithStackPostMicrotask`. Sources: [packages/next/src/server/app-render/dynamic-rendering.ts:84-112](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L84-L112) The `createDynamicTrackingState(isDebugDynamicAccesses)` function instantiates this tracking object with an empty `dynamicAccesses` array and null error states. When a dynamic scope is entered, `annotateDynamicAccess(expression, prerenderStore)` pushes a new entry into `dynamicAccesses`, capturing `new Error().stack` if `isDebugDynamicAccesses` is enabled. Sources: [packages/next/src/server/app-render/dynamic-rendering.ts:124-133](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L124-L133), [packages/next/src/server/app-render/dynamic-rendering.ts:612-625](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L612-L625) | Structure / Function | Type / Signature | Purpose | | :--- | :--- | :--- | | `DynamicAccess` | `object` | Holds optional stack trace and string expression for a dynamic read. | | `DynamicTrackingState` | `object` | Container tracking `isDebugDynamicAccesses`, `dynamicAccesses`, and sync error states. | | `createDynamicTrackingState` | `(isDebugDynamicAccesses?: boolean) => DynamicTrackingState` | Factory initializing a clean dynamic tracking state record. | | `annotateDynamicAccess` | `(expression: string, prerenderStore: PrerenderStoreModern | ValidationStoreClient) => void` | Appends a dynamic access record to the tracking state if present. | Sources: [packages/next/src/server/app-render/dynamic-rendering.ts:84-133](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L84-L133), [packages/next/src/server/app-render/dynamic-rendering.ts:612-625](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L612-L625) ### Postponed State Mechanics and Parsing Postponed state representations handle data and HTML segments that suspend during partial prerendering (PPR). The `DynamicState` enum distinguishes between RSC render data (`DATA = 1`) and HTML shell render phases (`HTML = 2`). Sources: [packages/next/src/server/app-render/postponed-state.ts:15-25](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/postponed-state.ts#L15-L25) | Enum / Value | Value / Properties | Meaning | | :--- | :--- | :--- | | `DynamicState.DATA` | `1` | Dynamic access occurred during the React Server Component render phase. | | `DynamicState.HTML` | `2` | Dynamic access occurred during the HTML shell render phase. | | `DynamicDataPostponedState` | `{ type: DynamicState.DATA, renderResumeDataCache }` | Postponed state for dynamic data payload. | | `DynamicHTMLPostponedState` | `{ type: DynamicState.HTML, data: [...], renderResumeDataCache }` | Postponed state containing prelude state, React postponed object, and cache. | Sources: [packages/next/src/server/app-render/postponed-state.ts:15-63](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/postponed-state.ts#L15-L63) The `parsePostponedState(state, interpolatedParams, maxPostponedStateSizeBytes)` function parses a serialized postponed state string by extracting the initial length match, slicing the postponed string payload and resume data cache, and replacing fallback route parameters using `getDynamicParam` when interpolated params are provided. Sources: [packages/next/src/server/app-render/postponed-state.ts:117-216](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/postponed-state.ts#L117-L216) > [!WARNING] > If parsing fails due to malformed string prefixes or JSON errors, `parsePostponedState` catches the exception, logs it, and falls back to a default `DynamicDataPostponedState` instance rather than crashing the request parser. Sources: [packages/next/src/server/app-render/postponed-state.ts:203-215](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/postponed-state.ts#L203-L215) ### Client Navigation Hook Dynamic Bailouts Client hooks such as `usePathname`, `useSearchParams`, `useParams`, `useSelectedLayoutSegments`, and `useSelectedLayoutSegment` invoke `useDynamicRouteParams` or `useDynamicSearchParams` during server-side rendering to signal runtime dependency. Sources: [packages/next/src/client/components/navigation.ts:65-66](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts#L65-L66), [packages/next/src/client/components/navigation.ts:122-123](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts#L122-L123), [packages/next/src/client/components/navigation.ts:225-226](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts#L225-L226), [packages/next/src/client/components/navigation.ts:277-280](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts#L277-L280), [packages/next/src/client/components/navigation.ts:332-335](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts#L332-L335) The dynamic hook invocation trace flows through the following call chain: 1. `useSelectedLayoutSegment()` calls `useSelectedLayoutSegments(parallelRouteKey)` — accesses layout context and retrieves parallel segment paths. 2. `useSelectedLayoutSegments()` invokes `useDynamicRouteParams('useSelectedLayoutSegments()')` — checks store types and manages cache components fallback parameters. 3. `useDynamicRouteParams()` evaluates work unit stores (`prerender-client`) and uses `makeClientHookHangingPromise` to suspend rendering when fallback parameters are present. Sources: [packages/next/src/server/app-render/dynamic-rendering.ts:627-642](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L627-L642), [packages/next/src/client/components/navigation.ts:332-337](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts#L332-L337) ```mermaid sequenceDiagram participant Nav as next/navigation participant Dyn as dynamic-rendering.ts participant Work as work-unit-async-storage Nav->>Dyn: useSelectedLayoutSegment() Dyn->>Nav: useSelectedLayoutSegments() Nav->>Dyn: useDynamicRouteParams('useSelectedLayoutSegment()') Dyn->>Work: workUnitAsyncStorage.getStore() Work-->>Dyn: prerender-client workUnitStore Dyn->>Dyn: makeClientHookHangingPromise() ``` Sources: [packages/next/src/server/app-render/dynamic-rendering.ts:627-642](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L627-L642), [packages/next/src/client/components/navigation.ts:332-337](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts#L332-L337) | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | Separation of `DynamicState.DATA` and `HTML` | Precise tracking of whether access occurred during RSC or HTML shell generation | Additional branch handling in postponed state parsers | | Hanging promises for client hooks in `prerender-client` | Allows components to suspend cleanly as dynamic holes during PPR | Requires robust abort signal listeners to reject pending hanging promises on timeout | | Fallback route param replacement strings | Enables serialization of postponed states with dynamic parameter segments | String manipulation overhead during state re-hydration | Sources: [packages/next/src/server/app-render/dynamic-rendering.ts:627-642](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L627-L642), [packages/next/src/server/app-render/postponed-state.ts:15-25](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/postponed-state.ts#L15-L25), [packages/next/src/server/app-render/postponed-state.ts:168-190](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/postponed-state.ts#L168-L190), [packages/next/src/server/dynamic-rendering-utils.ts:108-113](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dynamic-rendering-utils.ts#L108-L113) ## Dynamic Params and Staged Resolution ### Overview Parameter resolution within staged rendering bridges static shells and dynamic server execution. Route parameters (`params`) and search parameters (`searchParams`) are accessed via async promises that leverage staged progression controls to delay unblocking until specific render boundaries or segment stages are reached. Sources: [packages/next/src/server/request/params.ts:331-380](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts#L331-L380), [packages/next/src/server/app-render/vary-params.ts:21-35](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/vary-params.ts#L21-L35) ### Parameter Promise Lifecycle and Staged Resolution The lifecycle of parameter resolution coordinates execution across static and dynamic boundaries. When server routes evaluate parameters during prerendering, `createServerParamsForRoute` determines the appropriate execution path based on the active `WorkUnitStore` type. Sources: [packages/next/src/server/request/params.ts:138-204](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts#L138-L204) The call-chain execution walkthrough for parameter resolution proceeds as follows: 1. `createServerParamsForRoute` retrieves the current `workUnitStore` and dispatches static route params to `createStaticPrerenderParams`. Sources: [packages/next/src/server/request/params.ts:138-158](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts#L138-L158) 2. `createStaticPrerenderParams` inspects whether `__NEXT_APP_SHELLS` is active and invokes `stagedRendering.delayUntilStage` with the late static link data stage (`RenderStage.Static`). Sources: [packages/next/src/server/request/params.ts:359-380](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts#L359-L380), [packages/next/src/server/dynamic-rendering-utils.ts:198-199](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dynamic-rendering-utils.ts#L198-L199) 3. `delayUntilStage` obtains the underlying stage promise by invoking `this.getStagePromise(stage)`. Sources: [packages/next/src/server/app-render/staged-rendering.ts:372-377](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L372-L377) 4. `getStagePromise` returns `this.triggers[stage].promise`, which remains pending until the staged rendering controller advances past that specific milestone. Sources: [packages/next/src/server/app-render/staged-rendering.ts:364-366](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L364-L366) ```mermaid sequenceDiagram participant Route as params.ts participant Static as createStaticPrerenderParams participant Controller as StagedRenderingController participant Trigger as StageTrigger Route->>Static: createServerParamsForRoute() Static->>Controller: delayUntilStage(RenderStage.Static) Controller->>Trigger: getStagePromise(RenderStage.Static) Trigger-->>Controller: pending promise Controller-->>Route: delayed promise ``` Sources: [packages/next/src/server/request/params.ts:138-158](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts#L138-L158), [packages/next/src/server/request/params.ts:359-380](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts#L359-L380), [packages/next/src/server/app-render/staged-rendering.ts:364-377](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L364-L377) > [!NOTE] > Even when parameters are entirely static, they are intentionally excluded from the initial HTML shell by delaying their resolution until the static stage is reached. Sources: [packages/next/src/server/request/params.ts:359-380](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts#L359-L380) ### Vary-Params Tracking and Accumulators To support granular segment caching and flight serialization, Next.js tracks which parameter keys are accessed during rendering using `VaryParamsAccumulator` structures. Sources: [packages/next/src/server/app-render/vary-params.ts:21-35](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/vary-params.ts#L21-L35) | Accumulator Field / Function | Type / Return | Purpose | | :--- | :--- | :--- | | `varyParams` | `VaryParams` (`Set`) | Mutable set accumulating parameter property accesses during render | | `status` | `'pending' \| 'fulfilled'` | Tracks whether the thenable has finalized its vary parameter set | | `value` | `VaryParams` | Finalized parameter key set exposed to React Flight | | `createResponseVaryParamsAccumulator` | `ResponseVaryParamsAccumulator` | Initializes head, rootParams, and segment tracking sets | | `createVaryingParams` | `Params` | Wraps route parameters in getter properties or Proxies to record reads | Sources: [packages/next/src/server/app-render/vary-params.ts:21-105](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/vary-params.ts#L21-L105), [packages/next/src/server/app-render/vary-params.ts:244-298](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/vary-params.ts#L244-L298) > [!TIP] > When optional catch-all parameters are present (`[[...slug]]`), `createVaryingParams` employs a JavaScript `Proxy` to intercept `get`, `has`, and `ownKeys` traps, ensuring missing properties and enumerations correctly register as varying accesses. Sources: [packages/next/src/server/app-render/vary-params.ts:249-281](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/vary-params.ts#L249-L281) | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | `Object.defineProperty` getters for standard parameters | High engine optimization potential for property reads | Requires exact key enumeration upfront | | `Proxy` trap wrapping for optional catch-all parameters | Captures missing keys, `in` checks, and `Object.keys()` iterations | Higher runtime overhead compared to native property getters | | Singleton `emptyVaryParamsAccumulator` | Zero memory allocation and immediate resolution for static client components | Limited to parameter-free segments | Sources: [packages/next/src/server/app-render/vary-params.ts:71-91](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/vary-params.ts#L71-L91), [packages/next/src/server/app-render/vary-params.ts:244-298](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/vary-params.ts#L244-L298) ## Sync IO Tracking and Node Environment Extensions ### Overview Next.js intercepts synchronous platform operations (such as reads of current time, random number generators, or cryptographic functions) during pre-rendering to prevent non-deterministic values from being baked into static outputs. When a synchronous I/O action occurs within a pre-render or staged execution context, platform extensions capture the access, format specific diagnostic messages, and trigger stage interruptions. Sources: [packages/next/src/server/node-environment-extensions/io-utils.tsx:13-103](https://github.com/blade47/next.js/blob/main/packages/next/src/server/node-environment-extensions/io-utils.tsx#L13-L103), [packages/next/src/server/app-render/sync-io-messages.ts:33-85](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/sync-io-messages.ts#L33-L85) ### Synchronous Platform IO Interception and Call-Chain Execution When code executes inside a Node environment extension, `io()` intercepts calls using a three-argument signature: `io(expression: string, type: SyncIOApiType)`. The execution flows through work-unit storage checks and controller evaluation. ```mermaid sequenceDiagram participant Caller as User Code / Platform API participant IOExt as io-utils.tsx (`io`) participant WorkUnit as workUnitAsyncStorage participant Controller as StagedRenderingController Caller->>IOExt: io(expression, type) IOExt->>WorkUnit: getStore() WorkUnit-->>IOExt: workUnitStore alt workUnitStore.type is 'prerender' or 'prerender-runtime' IOExt->>IOExt: check prerenderSignal.aborted IOExt->>Dynamic: abortOnSynchronousPlatformIOAccess(...) else workUnitStore.type is 'request' IOExt->>Controller: shouldTrackSyncInterrupt() Controller-->>IOExt: true/false IOExt->>Controller: syncInterruptCurrentStageWithReason(syncIOError) end ``` Sources: [packages/next/src/server/node-environment-extensions/io-utils.tsx:13-103](https://github.com/blade47/next.js/blob/main/packages/next/src/server/node-environment-extensions/io-utils.tsx#L13-L103), [packages/next/src/server/app-render/staged-rendering.ts:154-185](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L154-L185) The detailed call chain proceeds as follows: 1. `io(expression, type)` retrieves `workUnitAsyncStorage` and `workAsyncStorage`. Sources: [packages/next/src/server/node-environment-extensions/io-utils.tsx:13-15](https://github.com/blade47/next.js/blob/main/packages/next/src/server/node-environment-extensions/io-utils.tsx#L13-L15) 2. Depending on `workUnitStore.type`, if it matches `'prerender'`, `'prerender-runtime'`, or `'prerender-client'`, it checks whether `prerenderSignal.aborted` is `false`. Sources: [packages/next/src/server/node-environment-extensions/io-utils.tsx:21-54](https://github.com/blade47/next.js/blob/main/packages/next/src/server/node-environment-extensions/io-utils.tsx#L21-L54) 3. It calls `abortOnSynchronousPlatformIOAccess(...)`, which records the stack via `applyOwnerStack(createSyncIOError(...))` into `dynamicTracking.syncDynamicErrorWithStack` and invokes `prerenderStore.controller.abort(error)`. Sources: [packages/next/src/server/app-render/dynamic-rendering.ts:323-342](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L323-L342), [packages/next/src/server/node-environment-extensions/io-utils.tsx:29-34](https://github.com/blade47/next.js/blob/main/packages/next/src/server/node-environment-extensions/io-utils.tsx#L29-L34) 4. If `workUnitStore.type` is `'request'`, it inspects `stageController.shouldTrackSyncInterrupt()`. Sources: [packages/next/src/server/node-environment-extensions/io-utils.tsx:55-57](https://github.com/blade47/next.js/blob/main/packages/next/src/server/node-environment-extensions/io-utils.tsx#L55-L57) 5. It creates either `createSyncIOError` or `createSyncIORuntimeError` based on whether the current stage is `Static`/`EarlyStatic` or `Runtime`, wraps it with `applyOwnerStack()`, and invokes `stageController.syncInterruptCurrentStageWithReason(syncIOError)`. Sources: [packages/next/src/server/node-environment-extensions/io-utils.tsx:58-77](https://github.com/blade47/next.js/blob/main/packages/next/src/server/node-environment-extensions/io-utils.tsx#L58-L77) Sources: [packages/next/src/server/node-environment-extensions/io-utils.tsx:13-103](https://github.com/blade47/next.js/blob/main/packages/next/src/server/node-environment-extensions/io-utils.tsx#L13-L103) > [!CAUTION] > During `EarlyRuntime` stages, synchronous I/O throws an error because the segment is runtime-prefetchable and an interruption would abort the prefetch prematurely, whereas `Runtime` stages permit synchronous I/O since non-prefetchable segments will never be runtime prefetched. Sources: [packages/next/src/server/app-render/staged-rendering.ts:168-177](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/staged-rendering.ts#L168-L177) ### Sync IO API Types and Diagnostic Messages Synchronous I/O tracking categorizes operations into specific API types, mapping each to dedicated documentation URLs and remediation guidance. | SyncIOApiType | Documentation Record Keys | Associated Remediation Docs | | :--- | :--- | :--- | | `time` | `SYNC_IO_DOCS.time`, `SYNC_IO_CLIENT_DOCS.time`, `SYNC_IO_RUNTIME_DOCS.time` | `blocking-prerender-current-time` (+ telemetry bullet for `performance.now()`) | | `random` | `SYNC_IO_DOCS.random`, `SYNC_IO_CLIENT_DOCS.random`, `SYNC_IO_RUNTIME_DOCS.random` | `blocking-prerender-random` | | `crypto` | `SYNC_IO_DOCS.crypto`, `SYNC_IO_CLIENT_DOCS.crypto`, `SYNC_IO_RUNTIME_DOCS.crypto` | `blocking-prerender-crypto` | Sources: [packages/next/src/server/app-render/sync-io-messages.ts:1-19](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/sync-io-messages.ts#L1-L19), [packages/next/src/server/app-render/sync-io-messages.ts:21-46](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/sync-io-messages.ts#L21-L46) > [!NOTE] > Server-side sync I/O errors recommend fixing the issue by adding a dynamic data access like `await connection()`, caching the value with `"use cache"`, or moving rendering to a Client Component. Client-side sync I/O errors suggest wrapping in `` or moving the read into a `useEffect` or event handler. Sources: [packages/next/src/server/app-render/sync-io-messages.ts:33-85](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/sync-io-messages.ts#L33-L85) ## Cache Coordination and Route Stream Generation ### Overview Cache coordination and route stream generation unify `use-cache` semantics, resume data cache serialization, and Flight stream generation. When handling cached or dynamic data dependencies within Next.js application render workflows, cache entries are evaluated for expiration, stale times, and dynamic omission before being embedded into the React Server Components (RSC) payload or staged execution pipelines. Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts:2837-2849](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L2837-L2849), [packages/next/src/server/app-render/app-render.tsx:914-964](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L914-L964) ### Cache Expiration and Dynamic Omission Semantics During static generation or prerendering, cache entries with a revalidate value of `0` or an expiration time under the dynamic expiration threshold are omitted from the static shell. This creates a dynamic hole filled during resume operations. Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts:2851-2892](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L2851-L2892) | WorkUnitStore Type | Behavior on Short Expiry / Revalidate `0` | Cache Signal & Deferred Action | Sources | | :--- | :--- | :--- | :--- | | `prerender` | Omitted from static shell; creates dynamic hole for resume | Ends cache signal read; resolves shared cache result as `prerender-dynamic` with a hanging promise | [packages/next/src/server/use-cache/use-cache-wrapper.ts:2856-2892](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L2856-L2892) | | `request` | Deferred to runtime or dynamic stage in development mode | Ends cache signal read (if unended); awaits devtools IO-aware promise for current or dynamic stage | [packages/next/src/server/use-cache/use-cache-wrapper.ts:2893-2917](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L2893-L2917) | | `prerender-runtime`, `prerender-ppr`, `cache`, `unstable-cache`, etc. | Passes through without explicit static shell omission | No direct transformation; governed by enclosing context | [packages/next/src/server/use-cache/use-cache-wrapper.ts:2918-2929](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L2918-L2929) | Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts:2856-2929](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L2856-L2929) > [!TIP] > When a cache entry's `revalidate` is set to `0` or its expiration falls below `DYNAMIC_EXPIRE`, the system avoids generating static pages for such data, replacing them with hanging promises that resolve via resume data caches. Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts:2851-2892](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L2851-L2892) ### Staged Dynamic Flight Render and Resume Data Serialization When production staged dynamic flight renders execute in Node.js streams, the request store initializes stale time trackers, stage controllers, vary params accumulators, and async API promises. If runtime prefetching is enabled via loader trees, a prerender resume data cache and cache signal are spawned. Sources: [packages/next/src/server/app-render/app-render.tsx:914-964](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L914-L964) The staged flight render follows a precise sequential task execution pipeline using `runInSequentialTasks`: 1. `stageController.advanceStage(RenderStage.ShellStatic)` advances the stage. Sources: [packages/next/src/server/app-render/app-render.tsx:1032-1036](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L1032-L1036) 2. `workUnitAsyncStorage.run(requestStore, renderToNodeFlightStream, ...)` generates the source node flight stream. Sources: [packages/next/src/server/app-render/app-render.tsx:1037-1044](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L1037-L1044) 3. `new ReplayableNodeStream(sourceStream)` creates replay streams for dynamic and static outputs, counting shell and static stage bytes. Sources: [packages/next/src/server/app-render/app-render.tsx:1046-1055](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L1046-L1055) 4. `stageController.advanceStage(RenderStage.Static)` moves execution into the static stage. Sources: [packages/next/src/server/app-render/app-render.tsx:1059-1061](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L1059-L1061) 5. `staleTimeIterable.close()` and `finishAccumulatingVaryParams(requestStore.varyParamsAccumulator)` flush tracking data. Sources: [packages/next/src/server/app-render/app-render.tsx:1062-1070](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L1062-L1070) 6. `stageController.advanceStage(RenderStage.Dynamic)` completes the progression into the dynamic stage. Sources: [packages/next/src/server/app-render/app-render.tsx:1071-1074](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L1071-L1074) Sources: [packages/next/src/server/app-render/app-render.tsx:1032-1074](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L1032-L1074) > [!WARNING] > Postponed states and resume data caches serialize into string formats incorporating payload length identifiers. If fallback route params are present, replacements are serialized and prepended to ensure parameter interpolation during resumption. Sources: [packages/next/src/server/app-render/postponed-state.ts:78-115](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/postponed-state.ts#L78-L115) ## Related - [[App Server Rendering]] - [[Prefetching and PPR]] --- ## Technical docs: Server Actions URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-router-rendering/server-actions
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/app-render/action-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts) - [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts) - [packages/next/src/server/dev/hot-reloader-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts) - [packages/next/src/server/app-render/encryption.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption.ts) - [packages/next/src/server/base-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts) - [packages/next/src/client/app-call-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-call-server.ts) - [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx) - [packages/next/src/client/form-shared.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/form-shared.tsx) - [packages/next/src/client/app-dir/form.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-dir/form.tsx) - [packages/next/src/server/app-render/react-server.node.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/react-server.node.ts) - [packages/next/src/server/lib/server-action-request-meta.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/server-action-request-meta.ts) - [packages/next/src/server/app-render/use-flight-response.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/use-flight-response.tsx) - [packages/next/src/server/request/params.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts) - [packages/next/src/client/app-index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx) - [packages/next/src/server/mcp/tools/get-server-action-by-id.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-server-action-by-id.ts) - [packages/next/src/client/components/app-router-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts) - [packages/next/src/client/form.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/form.tsx) - [packages/next/src/server/app-render/encryption-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption-utils.ts) - [packages/next/src/next-devtools/dev-overlay.browser.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx) - [packages/next/src/server/app-render/manifests-singleton.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/manifests-singleton.ts) - [packages/next/src/server/after/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/index.ts) - [packages/next/src/shared/lib/server-reference-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/server-reference-info.ts) - [packages/next/src/client/components/segment-cache/cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts)
## Overview Server Actions enable asynchronous functions defined on the server to be securely invoked from client components and form submissions, bridging client-side interactions and server-side mutations without requiring manual API route boilerplate. By handling execution pipelines, cryptographic bound argument verification, request routing, and router state reconciliation, Server Actions integrate seamlessly into Next.js rendering and caching systems. Sources: [packages/next/src/server/app-render/action-handler.ts:1300-1354](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1300-L1354), [packages/next/src/server/app-render/encryption.ts:48-96](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption.ts#L48-L96), [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:104-147](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L104-L147) ## Client Action Dispatch and Form Invocation ### Overview Client-side server action invocation starts through the `callServer` function, which wraps action identifiers and arguments into a React transition. When invoked, `callServer` delegates execution to the app router's action queue via `dispatchAppRouterAction`. Sources: [packages/next/src/client/app-call-server.ts:5-17](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-call-server.ts#L5-L17) ### Action Queue and Dispatch Execution The action queue manages asynchronous transitions, action ordering, and concurrency state through `AppRouterActionQueue` and `dispatchAction`. When an action payload is dispatched, a deferred promise is created and registered with React via `startTransition`. The call-chain execution walkthrough for processing actions proceeds through the following steps: `dispatchAction()` creates a deferred promise and `ActionQueueNode`, checks if `actionQueue.pending === null`, immediately invokes `runAction()` if empty, executes the action via `actionQueue.action()`, triggers `handleResult()` upon resolution, and advances the queue via `runRemainingActions()`. Sources: [packages/next/src/client/components/app-router-instance.ts:71-144](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L71-L144), [packages/next/src/client/components/app-router-instance.ts:146-216](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L146-L216) > [!NOTE] > Navigations (`ACTION_NAVIGATE` and `ACTION_RESTORE`) take immediate priority over pending actions. When a navigation action arrives while the queue is non-empty, the current pending action is marked as `discarded = true` so its state is never applied, and the navigation starts immediately while preserving subsequent queued nodes. > Sources: [packages/next/src/client/components/app-router-instance.ts:191-208](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L191-L208) ### Form Submissions and URL Handling Client-side form submissions are intercepted by `
` components across both the App and Pages routers. The submission handler validates action URLs, inspects submitter attributes, and extracts form data into search parameters or payloads. Sources: [packages/next/src/client/app-dir/form.tsx:147-224](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-dir/form.tsx#L147-L224), [packages/next/src/client/form.tsx:90-169](https://github.com/blade47/next.js/blob/main/packages/next/src/client/form.tsx#L90-L169) | Form Property / Attribute | Disallowed in `` | Purpose / Handling | | :--- | :--- | :--- | | `method` | Yes (`DISALLOWED_FORM_PROPS`) | Disallowed directly on ``; only `get` method is supported for navigating forms. | | `encType` | Yes (`DISALLOWED_FORM_PROPS`) | Disallowed directly on ``; only `application/x-www-form-urlencoded` is supported. | | `target` | Yes (`DISALLOWED_FORM_PROPS`) | Disallowed directly on ``; only `_self` target is supported. | Sources: [packages/next/src/client/form-shared.tsx:3-3](https://github.com/blade47/next.js/blob/main/packages/next/src/client/form-shared.tsx#L3-L3), [packages/next/src/client/form-shared.tsx:128-132](https://github.com/blade47/next.js/blob/main/packages/next/src/client/form-shared.tsx#L128-L132) > [!WARNING] > File inputs are only supported if the `` component's `action` prop is a function (Server Action). If `action` is a string URL, file inputs cannot be encoded as search parameters and trigger a development warning, falling back to the filename string value. > Sources: [packages/next/src/client/form-shared.tsx:83-96](https://github.com/blade47/next.js/blob/main/packages/next/src/client/form-shared.tsx#L83-L96) ## Bound Argument Cryptographic Security ### Overview Server actions often rely on closure-captured variables, known as bound arguments, which are serialized and transmitted between the client and server. To prevent tampering and ensure confidentiality, Next.js secures these closure arguments using authenticated symmetric encryption. The encryption pipeline leverages the Web Crypto API (`crypto.subtle`) with the `AES-GCM` algorithm, initialized using either an environment variable (`NEXT_SERVER_ACTIONS_ENCRYPTION_KEY`) or a manifest-provided key. Sources: [packages/next/src/server/app-render/encryption.ts:45-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption.ts#L45-L70), [packages/next/src/server/app-render/encryption-utils.ts:37-91](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption-utils.ts#L37-L91) ### Encryption and Integrity Verification Flow When encoding or decoding server action bound arguments, Next.js follows a strict serialization and cryptographic pipeline. The process guarantees both confidentiality via AES-GCM and integrity by prefixing the plaintext payload with the target action ID. The call-chain execution walkthrough for encrypting bound arguments proceeds as follows: `encryptActionBoundArgs()` retrieves `workUnitAsyncStorage` and client module manifests, serializes arguments via `renderToReadableStream` and `streamToString`, invokes `encodeActionBoundArg()`, generates 16 random initialization vector bytes via `crypto.getRandomValues()`, runs `encrypt()` with `AES-GCM` using `actionId + arg` as plaintext, and encodes the IV and ciphertext into base64 via `btoa()`. Sources: [packages/next/src/server/app-render/encryption.ts:72-96](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption.ts#L72-L96), [packages/next/src/server/app-render/encryption.ts:108-219](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption.ts#L108-L219), [packages/next/src/server/app-render/encryption-utils.ts:37-50](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption-utils.ts#L37-L50) Conversely, the decoding pipeline executes: `decodeActionBoundArg()` fetches the encryption key via `getActionEncryptionKey()`, decodes the base64 payload via `atob()`, extracts the first 16 bytes as initialization vector (`ivValue`) and remaining bytes as ciphertext (`payload`), decrypts via `decrypt()`, checks if decrypted text starts with `actionId` as a checksum invariant, and returns the sliced argument payload. Sources: [packages/next/src/server/app-render/encryption.ts:48-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption.ts#L48-L70), [packages/next/src/server/app-render/encryption-utils.ts:52-65](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption-utils.ts#L52-L65) > [!WARNING] > During decryption, Next.js explicitly verifies that the decrypted plaintext starts with the expected `actionId`. If this prefix check fails, it immediately throws an `Invalid Server Action payload: failed to decrypt.` error, preventing cross-action replay attacks and payload substitution. > Sources: [packages/next/src/server/app-render/encryption.ts:65-67](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption.ts#L65-L67) ### Cryptographic Utility and Manifest Configuration Encryption utilities depend on helper functions to bridge binary buffers and string representations, safely managing V8 argument limits and raw crypto keys. Sources: [packages/next/src/server/app-render/encryption-utils.ts:6-91](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption-utils.ts#L6-L91) | Utility Function | Parameters | Return Type | Purpose / Description | | :--- | :--- | :--- | :--- | | `arrayBufferToString` | `buffer: ArrayBuffer \| Uint8Array` | `string` | Converts byte buffers to binary strings, handling V8's 65,535 argument limit via `String.fromCharCode` batching or iteration. | | `stringToUint8Array` | `binary: string` | `Uint8Array` | Converts a binary string into a `Uint8Array` using character codes. | | `encrypt` | `key: CryptoKey, iv: Uint8Array, data: Uint8Array` | `Promise` | Encrypts data using `crypto.subtle.encrypt` with the `AES-GCM` algorithm. | | `decrypt` | `key: CryptoKey, iv: Uint8Array, data: Uint8Array` | `Promise` | Decrypts data using `crypto.subtle.decrypt` with the `AES-GCM` algorithm. | | `getActionEncryptionKey` | *None* | `Promise` | Resolves the encryption key from `NEXT_SERVER_ACTIONS_ENCRYPTION_KEY` or `serverActionsManifest.encryptionKey`, importing it via `crypto.subtle.importKey`. | Sources: [packages/next/src/server/app-render/encryption-utils.ts:6-91](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption-utils.ts#L6-L91) > [!NOTE] > The encryption key loaded via `getActionEncryptionKey()` is cached in the module-scoped variable `__next_loaded_action_key` to avoid redundant cryptographic key imports across requests. > Sources: [packages/next/src/server/app-render/encryption-utils.ts:4-4](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption-utils.ts#L4-L4), [packages/next/src/server/app-render/encryption-utils.ts:67-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/encryption-utils.ts#L67-L70) ## Server Request Discovery and Routing ### Overview Server request discovery and routing handles incoming HTTP requests to determine whether they qualify as Server Actions, parses identifying headers and multipart payload metadata, resolves action modules via global manifests, and routes execution to the appropriate worker runtime or runtime environment. Sources: [packages/next/src/server/lib/server-action-request-meta.ts:6-52](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/server-action-request-meta.ts#L6-L52), [packages/next/src/server/app-render/manifests-singleton.ts:181-224](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/manifests-singleton.ts#L181-L224) ### Action Request Detection and Header Parsing Incoming requests are inspected via `getServerActionRequestMetadata()` to extract header flags and payload structures. This function analyzes headers across standard Node `IncomingMessage`, `BaseNextRequest`, and web-standard `NextRequest` interfaces, checking for the `next-action` header and specific content types. Sources: [packages/next/src/server/lib/server-action-request-meta.ts:6-24](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/server-action-request-meta.ts#L6-L24) | Request Metadata Property | Condition / Check | Purpose / Description | | :--- | :--- | :--- | | `actionId` | `req.headers.get(ACTION_HEADER)` or `req.headers[ACTION_HEADER]` | Extracts the target server action identifier from headers. | | `isURLEncodedAction` | `req.method === 'POST' && contentType === 'application/x-www-form-urlencoded'` | Identifies URL-encoded POST actions (which later bail out in the action handler). | | `isMultipartAction` | `req.method === 'POST' && contentType?.startsWith('multipart/form-data')` | Identifies multipart form data submissions carrying action payloads. | | `isFetchAction` | `actionId !== undefined && typeof actionId === 'string' && req.method === 'POST'` | Identifies fetch-based action requests carrying a valid action header. | | `isPossibleServerAction` | `isFetchAction \|\| isURLEncodedAction \|\| isMultipartAction` | Aggregate boolean indicating whether the request should enter action routing. | Sources: [packages/next/src/server/lib/server-action-request-meta.ts:18-51](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/server-action-request-meta.ts#L18-L51) > [!NOTE] > URL-encoded actions are not fully supported by the action handler; however, they are permitted to flow through request metadata parsing to maintain consistent HTTP behavior when standard page components receive unexpected POST submissions. > Sources: [packages/next/src/server/lib/server-action-request-meta.ts:26-28](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/server-action-request-meta.ts#L26-L28) ### Manifests Singleton and Worker Resolution Next.js manages action bindings, server module maps, and client reference manifests globally via a `MANIFESTS_SINGLETON` symbol attached to `globalThis`. The `createServerModuleMap()` helper constructs a proxy that queries the server actions manifest for matching worker entries depending on the runtime environment (`edge` or `node`). Sources: [packages/next/src/server/app-render/manifests-singleton.ts:22-37](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/manifests-singleton.ts#L22-L37), [packages/next/src/server/app-render/manifests-singleton.ts:181-224](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/manifests-singleton.ts#L181-L224) When an action request targets a specific page, `selectWorkerForForwarding()` evaluates whether a worker exists for the current page name. If no worker matches the active page context, it falls back to selecting the first available worker capable of handling the action ID. Sources: [packages/next/src/server/app-render/manifests-singleton.ts:250-272](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/manifests-singleton.ts#L250-L272) ```mermaid graph TD A["Incoming Request"] --> B{"getServerActionRequestMetadata"} B -->|isPossibleServerAction: true| C["Check Action ID Header"] C -->|Missing Header| D["Throw InvariantError"] C -->|Valid Action ID| E["Query ServerModuleMap"] E --> F{"Worker Exists for Page?"} F -->|Yes| G["Dispatch to Local Worker"] F -->|No| H["selectWorkerForForwarding"] H --> I["Forward to Alternate Worker"] ``` Sources: [packages/next/src/server/lib/server-action-request-meta.ts:6-52](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/server-action-request-meta.ts#L6-L52), [packages/next/src/server/app-render/manifests-singleton.ts:181-224](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/manifests-singleton.ts#L181-L224), [packages/next/src/server/app-render/manifests-singleton.ts:250-272](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/manifests-singleton.ts#L250-L272) > [!WARNING] > If `getActionModIdOrError()` cannot locate an `actionId` in the server module map, it throws an error indicating potential deployment skew or an invalid action request from a different deployment version. > Sources: [packages/next/src/server/app-render/action-handler.ts:1361-1377](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1361-L1377), [packages/next/src/server/app-render/action-handler.ts:1379-1383](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1379-L1383) ## Action Execution and Render Pipeline ### Overview Once an incoming action request has been authenticated, its module ID verified, and its arguments decoded, the request enters the execution and render pipeline. This phase manages the runtime context, enforces argument limits, synchronizes cookies and revalidations across phases, and streams Flight response payloads back to the client. Sources: [packages/next/src/server/app-render/action-handler.ts:1300-1354](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1300-L1354), [packages/next/src/server/app-render/use-flight-response.tsx:35-154](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/use-flight-response.tsx#L35-L154) ### Action Execution Call-Chain The execution sequence is coordinated by `executeActionAndPrepareForRender()`, which manages the transition of request stores from the action phase to the render phase. Sources: [packages/next/src/server/app-render/action-handler.ts:1300-1354](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1300-L1354) ```mermaid graph TD A["executeActionAndPrepareForRender"] --> B["Set requestStore.phase = action"] B --> C{"args.length > 1000?"} C -->|Yes| D["Throw Error: Args List Too Long"] C -->|No| E["workUnitAsyncStorage.run run action.apply"] E --> F["Evaluate skipPageRendering condition"] F --> G["Finally Block: Switch phase to render"] G --> H["synchronizeMutableCookies"] H --> I["Update workStore.isDraftMode"] I --> J["executeRevalidates"] ``` Sources: [packages/next/src/server/app-render/action-handler.ts:1300-1354](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1300-L1354) > [!CAUTION] > The argument length check enforces `SERVER_ACTION_ARGS_LIMIT = 1000`. If an incoming payload supplies more than 1000 arguments, execution aborts immediately to prevent stack overflow faults during `action.apply()`. > Sources: [packages/next/src/server/app-render/action-handler.ts:1294-1319](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1294-L1319) ### Request Context and Phase Transition During execution, the request store phase is explicitly managed to handle cookies, draft mode toggles, and cache revalidations. Sources: [packages/next/src/server/app-render/action-handler.ts:1312-1352](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1312-L1352) | Phase Property / Hook | Target Store / Function | Purpose and Behavior | | :--- | :--- | :--- | | `requestStore.phase` | `'action'` transitioning to `'render'` | Switches the execution context mode when shifting from userspace action execution to subsequent page rendering. | | Cookie Synchronization | `synchronizeMutableCookies(requestStore)` | Updates immutable cookies in the render phase to reflect mutations performed via `cookies()` during the action phase. | | Draft Mode State | `workStore.isDraftMode` | Reflects toggles to draft mode performed in `requestStore.draftMode.isEnabled` for subsequent rendering. | | Tag/Path Revalidation | `executeRevalidates(workStore)` | Ensures all pending data revalidations called by `revalidateTag` or `revalidatePath` take effect before rendering. | Sources: [packages/next/src/server/app-render/action-handler.ts:1312-1352](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts#L1312-L1352) ### Flight Stream Processing and Inline Ingestion The pipeline streams Flight responses via `getFlightStream()`, which resolves client module mappings and runtime-specific stream adapters (Node streams or web `ReadableStream` instances). Sources: [packages/next/src/server/app-render/use-flight-response.tsx:35-154](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/use-flight-response.tsx#L35-L154) ```typescript export function getFlightStream( flightStream: Readable | BinaryStreamOf, debugStream: Readable | ReadableStream | undefined, debugEndTime: number | undefined, nonce: string | undefined ): Promise ``` Sources: [packages/next/src/server/app-render/use-flight-response.tsx:35-40](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/use-flight-response.tsx#L35-L40) To inject hydration data outside standard React rendering, `createInlinedDataReadableStream()` wraps the Flight stream and formats chunks into inline script instructions using specialized bootstrap payloads. Sources: [packages/next/src/server/app-render/use-flight-response.tsx:165-220](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/use-flight-response.tsx#L165-L220) | Payload Type Constant | Value | Role in Stream Ingestion | | :--- | :--- | :--- | | `INLINE_FLIGHT_PAYLOAD_BOOTSTRAP` | `0` | Initializes the `self.__next_f` chunk array instruction queue. | | `INLINE_FLIGHT_PAYLOAD_DATA` | `1` | Enqueues standard string-encoded Flight data chunks. | | `INLINE_FLIGHT_PAYLOAD_FORM_STATE` | `2` | Serializes form state states alongside initial boot instructions. | | `INLINE_FLIGHT_PAYLOAD_BINARY` | `3` | Encodes arbitrary binary chunk buffers in base64 format for safe script tag embedding. | Sources: [packages/next/src/server/app-render/use-flight-response.tsx:14-18](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/use-flight-response.tsx#L14-L18), [packages/next/src/server/app-render/use-flight-response.tsx:227-266](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/use-flight-response.tsx#L227-L266) > [!NOTE] > If a chunk cannot be decoded as a valid UTF-8 string due to embedded binary payloads, `createInlinedDataReadableStream()` falls back to serializing the buffer as base64 via `Buffer.from()` or `btoa()` inside an `INLINE_FLIGHT_PAYLOAD_BINARY` instruction. > Sources: [packages/next/src/server/app-render/use-flight-response.tsx:191-206](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/use-flight-response.tsx#L191-L206), [packages/next/src/server/app-render/use-flight-response.tsx:256-266](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/use-flight-response.tsx#L256-L266) ## Client State Invalidation and Reconciliation ### Overview When a server action completes and returns its response payload, the client-side router reducer handles cache invalidation and state tree reconciliation. The reducer inspects the revalidation status returned by the server and purges stale caches to maintain data consistency. Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:311-352](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L311-L352) ### Call-Chain Execution Walkthrough The invalidation flow triggered by a server action response proceeds through a precise sequence of function calls to notify registered tasks and listeners: 1. `serverActionReducer` — Receives the action response state and checks if `revalidationKind !== ActionDidNotRevalidate`. 2. `invalidateEntirePrefetchCache` — Increments both route and segment cache version counters (`currentRouteCacheVersion++`, `currentSegmentCacheVersion++`) and hands off to visible link pinging and listener notification. 3. `pingInvalidationListeners` — Checks whether `invalidationListeners` is non-null, iterates over registered prefetch tasks, and evaluates `isPrefetchTaskDirty()`. 4. `notifyInvalidationListener` — Extracts the `onInvalidate` callback from the task, clears it to prevent duplicate execution, and safely invokes the user-space function inside a `try/catch` block. Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:311-352](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L311-L352), [packages/next/src/client/components/segment-cache/cache.ts:344-353](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L344-L353), [packages/next/src/client/components/segment-cache/cache.ts:404-441](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L404-L441) ```mermaid sequenceDiagram participant SAR as serverActionReducer participant IPC as invalidateEntirePrefetchCache participant PIL as pingInvalidationListeners participant NIL as notifyInvalidationListener SAR->>IPC: invalidateEntirePrefetchCache(nextUrl, tree) IPC->>PIL: pingInvalidationListeners(nextUrl, tree) PIL->>NIL: notifyInvalidationListener(task) ``` Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:311-352](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L311-L352), [packages/next/src/client/components/segment-cache/cache.ts:344-353](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L344-L353), [packages/next/src/client/components/segment-cache/cache.ts:404-441](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L404-L441) ### Revalidation Constants and Cache Invalidation Options The router distinguishes between different forms of revalidation to optimize whether route structures, dynamic data, or static segments are cleared from memory. Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:64-68](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L64-L68) | Revalidation Constant | Value / Type | Meaning and Behavior | | :--- | :--- | :--- | | `ActionDidNotRevalidate` | `0` | Indicates the server action triggered no data mutations or revalidations. | | `ActionDidRevalidateDynamicOnly` | `1` | Indicates revalidation was restricted to dynamic data sources. | | `ActionDidRevalidateStaticAndDynamic` | `2` | Indicates both static and dynamic cache entries must be invalidated. | Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:64-68](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L64-L68) ### Reconciliation Architecture and Trade-Offs | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | Lazy cache eviction via version increments (`currentRouteCacheVersion++`) | Avoids expensive synchronous garbage collection sweeps across all active entries upon revalidation. | Stale entries remain in memory until actively read and evicted. | | Single global set for `invalidationListeners` | Low memory overhead and simple registration model when no granular path-based tags exist. | Scans all registered tasks on invalidation rather than filtering by affected segments. | | Clearing `onInvalidate` callbacks immediately upon invocation | Guarantees user-space listeners execute at most once per invalidation event. | Requires tasks to re-register listeners if subsequent invalidations need tracking. | Sources: [packages/next/src/client/components/segment-cache/cache.ts:316-353](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L316-L353), [packages/next/src/client/components/segment-cache/cache.ts:404-440](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L404-L440) > [!WARNING] > Cache invalidation does not eagerly evict items from memory. Instead, version counters (`currentRouteCacheVersion` and `currentSegmentCacheVersion`) are incremented, causing entries to be treated as stale and lazily evicted only when next read. > Sources: [packages/next/src/client/components/segment-cache/cache.ts:321-326](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L321-L326) ## Development Tooling and MCP Diagnostics ### Overview The Next.js development server integrates specialized tooling for action compilation diagnostics, error overlay propagation, hot reloader management, and Model Context Protocol (MCP) inspection. Development tooling bridges server-side compilation issues with client-side runtime feedback through dedicated hmr channels and dispatcher actions. Sources: [packages/next/src/server/dev/hot-reloader-turbopack.ts:1577-1622](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts#L1577-L1622), [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:223-272](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx#L223-L272) ### Dev Overlay and Error Propagation When compilation or runtime errors occur during server action evaluation or page rendering, the development overlay queues and dispatches actions across the browser boundary using `createQueuable` wrappers. The error dispatcher standardizes state transitions such as build errors, unhandled rejections, and overlay toggles before rendering within a Shadow DOM container. Sources: [packages/next/src/next-devtools/dev-overlay.browser.tsx:130-240](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx#L130-L240), [packages/next/src/next-devtools/dev-overlay.browser.tsx:253-331](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx#L253-L331) > [!NOTE] > Events dispatched before React mounts the dispatcher are buffered in a queue and replayed via `replayQueuedEvents` once `maybeDispatch` becomes active in `useInsertionEffect`. > Sources: [packages/next/src/next-devtools/dev-overlay.browser.tsx:128-142](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx#L128-L142), [packages/next/src/next-devtools/dev-overlay.browser.tsx:293-307](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx#L293-L307) ### MCP Server Action Inspection Tools The MCP diagnostics server exposes tools such as `get_server_action_by_id` to inspect compiled server references directly from the build output directory. The tool queries `server-reference-manifest.json` to resolve execution metadata for node and edge runtime environments. Sources: [packages/next/src/server/mcp/tools/get-server-action-by-id.ts:22-156](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-server-action-by-id.ts#L22-L156) | Field Name | Type | Description | | :--- | :--- | :--- | | `actionId` | `string` | Unique identifier corresponding to the server reference key in the manifest. | | `runtime` | `string` | Execution environment target, resolved as either `'node'` or `'edge'`. | | `filename` | `string` | Absolute or relative source file path containing the action definition. | | `functionName` | `string` | Exported JavaScript function name, or `'inline server action'` if prefixed with `$$RSC_SERVER_ACTION_`. | Sources: [packages/next/src/server/mcp/tools/get-server-action-by-id.ts:7-130](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-server-action-by-id.ts#L7-L130) ## Related - [[App Server Rendering]] - [[Router State Reducer]] --- ## Technical docs: Route Handlers URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-router-rendering/route-handlers
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/route-modules/app-route/module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts) - [packages/next/src/server/base-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts) - [packages/next/src/server/api-utils/node/api-resolver.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts) - [packages/next/src/server/app-render/action-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts) - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/server/route-modules/pages/pages-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/pages/pages-handler.ts) - [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts) - [packages/next/src/server/lib/router-utils/resolve-routes.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts) - [packages/next/src/server/lib/router-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-server.ts) - [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts) - [packages/next/src/server/dev/next-dev-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts) - [packages/next/src/export/routes/app-route.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts) - [packages/next/src/server/route-modules/app-page/module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-page/module.ts) - [packages/next/src/server/route-modules/pages-api/module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/pages-api/module.ts) - [packages/next/src/server/api-utils/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts) - [packages/next/src/server/api-utils/node/parse-body.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/parse-body.ts) - [packages/next/src/server/api-utils/node/try-get-preview-data.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/try-get-preview-data.ts) - [packages/next/src/server/web/spec-extension/adapters/headers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts)
## Overview Route Handlers in Next.js manage backend data endpoints and request dispatching across distinct execution runtimes and application models. They bridge incoming HTTP traffic with userland route logic, balancing modern Web API Request and Response primitives in App Routes with legacy Node.js message handlers in Pages API routes. By orchestrating payload parsing, header adaptation, security checks, and prerender state tracking, these server modules handle request lifecycles uniformly across environments. Sources: [packages/next/src/server/route-modules/app-route/module.ts:793-978](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L793-L978), [packages/next/src/server/api-utils/node/api-resolver.ts:331-489](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L331-L489), [packages/next/src/server/web/edge-route-module-wrapper.ts:84-177](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L84-L177) ## App Route Module Architecture ### Overview App Route modules coordinate the complete lifecycle of incoming HTTP requests for App Router endpoints, managing asynchronous module loading, HTTP method resolution, dynamic bailout checks, and response validation. When a request arrives, the route module initializes request storage, evaluates static generation constraints, and executes the target userland handler within nested `AsyncLocalStorage` contexts. Sources: [packages/next/src/server/route-modules/app-route/module.ts:793-978](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L793-L978) ### Execution Lifecycle and Call Chain The execution pipeline processes incoming requests through a strict sequence of validation, store initialization, and tracer spans before invoking the userland handler. `AppRouteRouteModule.handle()` → `this.ensureUserland()` → `resolveHandlerFromUserland()` or `resolveHandler()` → `getImplicitTags()` → `createRequestStoreForAPI()` → `createWorkStore()` → `actionAsyncStorage.run()` → `workUnitAsyncStorage.run()` → `workAsyncStorage.run()` → `tracer.trace()` → `this.do()` Sources: [packages/next/src/server/route-modules/app-route/module.ts:793-952](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L793-L952) During this flow, `ensureUserland()` guarantees that modules utilizing top-level `await` are fully resolved before execution. Next, `handle()` checks whether non-static methods are present and applies dynamic configuration rules. Sources: [packages/next/src/server/route-modules/app-route/module.ts:793-878](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L793-L878) ### HTTP Method Resolution Incoming HTTP methods are normalized and matched against exported userland handlers using `resolveHandler(method: string)` or `resolveHandlerFromUserland()`. To prevent Remote Code Execution (RCE), requests with unrecognized HTTP methods are intercepted immediately. Sources: [packages/next/src/server/route-modules/app-route/module.ts:391-396](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L391-L396), [packages/next/src/server/route-modules/app-route/module.ts:813-816](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L813-L816) | Method Check / Resolver | Fallback Behavior | Target Method / Module | Sources | |-------------------------|-------------------|------------------------|---------| | `isHTTPMethod(method)` | Returns `400` status with `null` body | Unrecognized methods | [packages/next/src/server/route-modules/app-route/module.ts:391-396](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L391-L396) | | Live Userland HMR lookup | Fallfalls back to cached `_userland` module | `liveUserland` or `this._userland` | [packages/next/src/server/route-modules/app-route/module.ts:807-816](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L807-L816) | Sources: [packages/next/src/server/route-modules/app-route/module.ts:391-396](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L391-L396), [packages/next/src/server/route-modules/app-route/module.ts:807-816](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L807-L816) ### Dynamic Bailout Checks and Configuration App Route modules evaluate static generation options via `export const dynamic` configurations. The route execution runtime inspects the dynamic mode and modifies the incoming request object or throws a `DynamicServerError` when static generation rules are violated. Sources: [packages/next/src/server/route-modules/app-route/module.ts:869-926](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L869-L926) | Dynamic Configuration Value | Store Flag Modification | Request Transformation | Sources | |-----------------------------|-------------------------|------------------------|---------| | `'force-dynamic'` | `workStore.forceDynamic = true` | Unmodified request (`req`) | [packages/next/src/server/route-modules/app-route/module.ts:890-902](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L890-L902) | | `'force-static'` | `workStore.forceStatic = true` | Proxied with `forceStaticRequestHandlers` | [packages/next/src/server/route-modules/app-route/module.ts:903-910](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L903-L910) | | `'error'` | `workStore.dynamicShouldError = true` | Proxied with `requireStaticRequestHandlers` (if static gen) | [packages/next/src/server/route-modules/app-route/module.ts:911-917](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L911-L917) | | `'auto'` / `undefined` | Tracks dynamic access via store | Proxied via `proxyNextRequest(req, workStore)` | [packages/next/src/server/route-modules/app-route/module.ts:918-923](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L918-L923) | Sources: [packages/next/src/server/route-modules/app-route/module.ts:889-926](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L889-L926) > [!WARNING] > Exporting non-static HTTP methods such as `POST`, `PUT`, `DELETE`, `PATCH`, or `OPTIONS` via `hasNonStaticMethods()` will automatically trigger a `DynamicServerError` if the route is evaluated during static generation. Sources: [packages/next/src/server/route-modules/app-route/module.ts:866-878](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L866-L878), [packages/next/src/server/route-modules/app-route/module.ts:990-999](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L990-L999) ## Edge Runtime Execution Wrapper ### Overview The `EdgeRouteModuleWrapper` class adapts an `AppRouteRouteModule` for execution inside Edge runtimes, managing request parsing, dynamic route parameter normalization, cache handler initialization, and streaming response body consumption via `CloseController`. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:35-50](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L35-L50) ### Execution Lifecycle and Call-Chain When an Edge request arrives, `EdgeRouteModuleWrapper.wrap()` instantiates the wrapper and returns an `EdgeHandler` adapter function. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:61-82](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L61-L82) The execution request proceeds through the internal handler pipeline: `EdgeRouteModuleWrapper.handler()` → `getServerUtils()` → `routeModule.getNextConfigEdge()` → `initializeCacheHandlers()` → `normalizeDynamicRouteParams()` → `routeModule.handle()` → `trackStreamConsumed()` / `closeController.dispatchClose()`. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:84-176](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L84-L176) 1. `getServerUtils()` builds URL matching utilities using `matcher.isDynamic` and `matcher.definition.pathname`. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:88-96](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L88-L96) 2. `routeModule.getNextConfigEdge()` reads configuration settings for cache limits and life profiles. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:98-100](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L98-L100) 3. `initializeCacheHandlers()` and `setCacheHandler()` configure runtime memory limits and custom cache adapters. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:101-104](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L101-L104) 4. `normalizeDynamicRouteParams()` parses search parameters into dynamic route parameters. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:106-109](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L106-L109) 5. `routeModule.handle()` executes userland handler logic using constructed context parameters. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:116-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L116-L151) > [!NOTE] > If a response has no body, `setTimeout()` triggers `closeController.dispatchClose()` asynchronously. For streaming responses, `trackStreamConsumed()` wraps `res.body` to invoke `closeController.dispatchClose()` upon consumption. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:159-174](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L159-L174) Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:84-176](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L84-L176) ### Edge Runtime Configuration Context The `AppRouteRouteHandlerContext` passed to `routeModule.handle()` configures runtime rendering options and feature flags specifically tailored for Edge environments. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:116-148](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L116-L148) | Render Option Property | Value | Purpose / Edge Behavior | Sources | |------------------------|-------|--------------------------|---------| | `supportsDynamicResponse` | `true` | Enables dynamic response handling | [packages/next/src/server/web/edge-route-module-wrapper.ts:121-122](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L121-L122) | | `cacheComponents` | `!!process.env.__NEXT_CACHE_COMPONENTS` | Evaluates cache component feature flag | [packages/next/src/server/web/edge-route-module-wrapper.ts:126-126](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L126-L126) | | `validationLevel` | `'warning'` | Fallback validation level; instant validation is skipped | [packages/next/src/server/web/edge-route-module-wrapper.ts:127-130](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L127-L130) | | `experimental.authInterrupts` | `!!process.env.__NEXT_EXPERIMENTAL_AUTH_INTERRUPTS` | Sets auth interrupts experimental flag | [packages/next/src/server/web/edge-route-module-wrapper.ts:131-133](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L131-L133) | | `experimental.useCacheTimeout` | `0` | Sentinel value; cache fill times out immediately if read | [packages/next/src/server/web/edge-route-module-wrapper.ts:134-137](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L134-L137) | | `cacheLifeProfiles` | `nextConfig.cacheLife` | Configures cache life profiles from next config | [packages/next/src/server/web/edge-route-module-wrapper.ts:138-138](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L138-L138) | | `staticPageGenerationTimeout` | `0` | Sentinel value; static generation does not run in Edge | [packages/next/src/server/web/edge-route-module-wrapper.ts:139-142](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L139-L142) | Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:116-148](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L116-L148) > [!CAUTION] > Both `useCacheTimeout` and `staticPageGenerationTimeout` are hardcoded to `0` in Edge route contexts because Cache Components and static generation are unsupported in the Edge runtime. If invoked, they act as sentinels to immediately surface unexpected access errors. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:131-143](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L131-L143) ## Pages API Resolver Architecture ### Overview The legacy Pages API infrastructure bridges traditional Node.js request-response lifecycles and Next.js route handling via `PagesAPIRouteModule` and `apiResolver`. When a Pages API request is processed, `PagesAPIRouteModule.render()` initializes performance tracing through `wrapApiHandler()` and delegates directly to `apiResolver()`, supplying the userland module, context properties, and error callbacks. Sources: [packages/next/src/server/api-utils/index.ts:22-37](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L22-L37), [packages/next/src/server/route-modules/pages-api/module.ts:115-161](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/pages-api/module.ts#L115-L161) ### Execution Lifecycle and Call Chain The resolution flow executes through a deterministic pipeline from module instantiation down to userland handler invocation and telemetry reporting. `PagesAPIRouteModule` constructor → `wrapApiHandler()` → `PagesAPIRouteModule.render()` → `apiResolver()` → `parseBody()` → `resolver(req, res)` → `onError?.()` Sources: [packages/next/src/server/api-utils/index.ts:22-37](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L22-L37), [packages/next/src/server/api-utils/node/api-resolver.ts:331-489](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L331-L489), [packages/next/src/server/route-modules/pages-api/module.ts:115-161](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/pages-api/module.ts#L115-L161) 1. **Module Construction & Wrapping:** `PagesAPIRouteModule` validates that `options.userland.default` is a function and wraps `apiResolver` using `wrapApiHandler()` to establish root span attributes and trace execution under `NodeSpan.runHandler`. Sources: [packages/next/src/server/api-utils/index.ts:22-37](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L22-L37), [packages/next/src/server/route-modules/pages-api/module.ts:115-128](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/pages-api/module.ts#L115-L128) 2. **Context Setup & Lazy Property Injection:** `apiResolver` extracts page configuration (`resolverModule.config`), attaches lazy cookie parsers, defines writable `req.query` properties to support Express 5 compatibility, and configures preview data getters. Sources: [packages/next/src/server/api-utils/node/api-resolver.ts:351-376](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L351-L376) 3. **Payload Parsing & Response Interception:** If `bodyParser` is enabled and `apiReq.body` is unparsed, `parseBody()` processes the payload. Meanwhile, `apiRes.write` and `apiRes.end` are monkey-patched to track cumulative content length against `responseLimit`. Sources: [packages/next/src/server/api-utils/node/api-resolver.ts:377-409](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L377-L409) 4. **Handler Invocation & Error Handling:** The interop-defaulted resolver is called with `(req, res)`. If execution throws an `ApiError` or unhandled exception, `onError` telemetry is notified, and appropriate error responses are dispatched based on `dev` and `propagateError` flags. Sources: [packages/next/src/server/api-utils/node/api-resolver.ts:428-488](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L428-L488) > [!WARNING] > Returning a Web API `Response` object from a Pages API route in the Node.js runtime throws an explicit error instructing developers to use `runtime: "edge"` instead. Sources: [packages/next/src/server/api-utils/node/api-resolver.ts:439-444](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L439-L444) ### Page Configuration Options API routes export a configuration object (`PageConfig`) that dictates runtime behavior inside `apiResolver`. | Configuration Key | Type | Default Value | Purpose / Behavior | Sources | |-------------------|------|---------------+--------------------+---------| | `api.bodyParser` | `boolean \| { sizeLimit?: string \| number }` | `true` | Controls automatic JSON/urlencoded body parsing; set to `false` to consume raw streams | [packages/next/src/server/api-utils/node/api-resolver.ts:352-352](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L352-L352) | | `api.responseLimit` | `boolean \| string` | `true` (`4MB`) | Configures the maximum response size payload before logging a performance warning | [packages/next/src/server/api-utils/node/api-resolver.ts:353-353](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L353-L353) | | `api.externalResolver` | `boolean` | `false` | Flags whether another middleware or external library handles response termination, suppressing stalled-request warnings | [packages/next/src/server/api-utils/node/api-resolver.ts:354-354](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L354-L354) | Sources: [packages/next/src/server/api-utils/node/api-resolver.ts:351-355](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L351-L355) ### Design Trade-offs | Design Choice | Benefit | Cost | Sources | |---------------|---------|------|---------| | **Response Method Patching** | Tracks payload byte length transparently without altering userland code signatures | Overrides core `http.ServerResponse` prototype methods (`write`, `end`) per request | [packages/next/src/server/api-utils/node/api-resolver.ts:387-409](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L387-L409) | | **Lazy Property Evaluation** | Defers expensive cookie parsing and preview data decryption until property access | Introduces getter overhead via `Object.defineProperty` descriptors on request objects | [packages/next/src/server/api-utils/node/api-resolver.ts:357-375](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L357-L375), [packages/next/src/server/api-utils/index.ts:211-231](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L211-L231) | | **Development Stalled-Request Check** | Detects forgotten responses early during local development | Relies on timing heuristics and pipe-event listeners (`wasPiped`) which may produce false positives | [packages/next/src/server/api-utils/node/api-resolver.ts:429-434](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L429-L434), [packages/next/src/server/api-utils/node/api-resolver.ts:450-454](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L450-L454) | Sources: [packages/next/src/server/api-utils/node/api-resolver.ts:357-454](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L357-L454), [packages/next/src/server/api-utils/index.ts:211-231](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L211-L231) ## Request Parsing and Body Processing ### Overview Pages API request ingestion and body processing coordinate through `apiResolver` and `parseBody` to read incoming streams, enforce size limits, and parse payloads according to content-type headers. Sources: [packages/next/src/server/api-utils/node/api-resolver.ts:377-385](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L377-L385), [packages/next/src/server/api-utils/node/parse-body.ts:29-66](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/parse-body.ts#L29-L66) ### Call-Chain Execution Walkthrough 1. `apiResolver`: Evaluates page configuration to check if `bodyParser` is enabled (`config.api?.bodyParser !== false`). If enabled and `apiReq.body` is unpopulated, it invokes `parseBody` with the configured size limit or defaults to `'1mb'`. Sources: [packages/next/src/server/api-utils/node/api-resolver.ts:351-352](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L351-L352), [packages/next/src/server/api-utils/node/api-resolver.ts:378-384](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L378-L384) 2. `parseBody`: Parses the `content-type` header, extracts the character set encoding (defaulting to `utf-8`), and reads the raw request buffer via `getRawBody` up to the specified size limit. It converts the buffer to a string and branches based on the media type, handing JSON bodies over to `parseJson`. Sources: [packages/next/src/server/api-utils/node/parse-body.ts:29-65](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/parse-body.ts#L29-L65) 3. `parseJson`: Inspects the string length. If empty, it returns an empty object `{}` as a special-case fallback for client-side mistakes; otherwise, it executes `JSON.parse`. Sources: [packages/next/src/server/api-utils/node/parse-body.ts:12-23](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/parse-body.ts#L12-L23) 4. `ApiError`: Thrown when parsing fails or payload sizes exceed limits. Entity size errors throw an `ApiError` with status `413` (`Body exceeded limit`), malformed JSON throws status `400` (`Invalid JSON`), and general body errors throw status `400` (`Invalid body`). Sources: [packages/next/src/server/api-utils/node/parse-body.ts:21-22](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/parse-body.ts#L21-L22), [packages/next/src/server/api-utils/node/parse-body.ts:49-53](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/parse-body.ts#L49-L53) ```mermaid sequenceDiagram participant api-resolver.ts participant parse-body.ts participant index.ts api-resolver.ts->>parse-body.ts: parseBody(apiReq, limit) parse-body.ts->>parse-body.ts: parseJson(body) parse-body.ts->>index.ts: throw new ApiError(statusCode, message) ``` Sources: [packages/next/src/server/api-utils/node/api-resolver.ts:377-385](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L377-L385), [packages/next/src/server/api-utils/node/parse-body.ts:12-66](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/parse-body.ts#L12-L66), [packages/next/src/server/api-utils/index.ts:176-183](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L176-L183) ### Content Type Parsing Matrix | Media Type Match | Parsing Mechanism | Return Output | Sources | |------------------|-------------------|---------------+---------| | `application/json`, `application/ld+json` | `parseJson()` via `JSON.parse` | Object / Parsed JSON | [packages/next/src/server/api-utils/node/parse-body.ts:58-59](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/parse-body.ts#L58-L59) | | `application/x-www-form-urlencoded` | `querystring.decode()` | Key-value dictionary | [packages/next/src/server/api-utils/node/parse-body.ts:60-62](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/parse-body.ts#L60-L62) | | Other types (fallback) | Raw string conversion | `string` | [packages/next/src/server/api-utils/node/parse-body.ts:63-65](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/parse-body.ts#L63-L65) | Sources: [packages/next/src/server/api-utils/node/parse-body.ts:58-65](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/parse-body.ts#L58-L65) > [!WARNING] > If `raw-body` throws an error where `e.type` equals `'entity.too.large'`, `parseBody` catches it and raises an `ApiError` with status code `413`. Any other failure yields status code `400` with message `'Invalid body'`. Sources: [packages/next/src/server/api-utils/node/parse-body.ts:48-54](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/parse-body.ts#L48-L54) ## Preview Data and Context Resolution ### Overview Preview data resolution and request header adapters handle the decryption of preview cookies, evaluation of on-demand revalidation flags, and normalization of Node.js raw request headers into web-standard interfaces. The `apiResolver` function initializes lazy properties on the incoming request object for cookies, query parameters, preview data, and draft mode state. When `previewData` is accessed, it invokes `tryGetPreviewData`, which inspects incoming headers to determine if an on-demand revalidation request is underway. Sources: [packages/next/src/server/api-utils/node/api-resolver.ts:356-376](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L356-L376), [packages/next/src/server/api-utils/node/try-get-preview-data.ts:17-30](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/try-get-preview-data.ts#L17-L30) ### Call-Chain Execution Walkthrough 1. `apiResolver` sets up lazy evaluation for `previewData` by calling `tryGetPreviewData(req, res, apiContext, !!apiContext.multiZoneDraftMode)`. Sources: [packages/next/src/server/api-utils/node/api-resolver.ts:367-369](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L367-L369) 2. `tryGetPreviewData` inspects request headers by calling `checkIsOnDemandRevalidate(req.headers, options)`. Sources: [packages/next/src/server/api-utils/node/try-get-preview-data.ts:25-28](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/try-get-preview-data.ts#L25-L28) 3. `checkIsOnDemandRevalidate` verifies whether `rawHeaders.get` exists, and if so, invokes `HeadersAdapter.from(rawHeaders)` to wrap standard headers. Sources: [packages/next/src/server/api-utils/index.ts:86-87](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L86-L87) 4. `HeadersAdapter.from` checks if the input is already an instance of `Headers`; if it is a plain `IncomingHttpHeaders` object, it instantiates and returns a new `HeadersAdapter`. Sources: [packages/next/src/server/web/spec-extension/adapters/headers.ts:155-159](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L155-L159) 5. `checkIsOnDemandRevalidate` subsequently invokes `.get()` on the header collection, which calls `HeadersAdapter.prototype.get` to retrieve header values, executing `this.merge(value)` if multiple values exist as an array. Sources: [packages/next/src/server/api-utils/index.ts:89-90](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L89-L90), [packages/next/src/server/web/spec-extension/adapters/headers.ts:143-147](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L143-L147), [packages/next/src/server/web/spec-extension/adapters/headers.ts:176-181](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L176-L181) ```mermaid sequenceDiagram participant api-resolver.ts participant try-get-preview-data.ts participant index.ts participant headers.ts api-resolver.ts->>try-get-preview-data.ts: tryGetPreviewData(req, res, apiContext, multiZoneDraftMode) try-get-preview-data.ts->>index.ts: checkIsOnDemandRevalidate(req.headers, options) index.ts->>headers.ts: HeadersAdapter.from(rawHeaders) headers.ts->>headers.ts: HeadersAdapter.prototype.get(name) headers.ts->>headers.ts: HeadersAdapter.prototype.merge(value) ``` Sources: [packages/next/src/server/api-utils/node/api-resolver.ts:367-369](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/api-resolver.ts#L367-L369), [packages/next/src/server/api-utils/node/try-get-preview-data.ts:25-28](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/try-get-preview-data.ts#L25-L28), [packages/next/src/server/api-utils/index.ts:86-87](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L86-L87), [packages/next/src/server/web/spec-extension/adapters/headers.ts:143-147](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L143-L147), [packages/next/src/server/web/spec-extension/adapters/headers.ts:155-159](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L155-L159), [packages/next/src/server/web/spec-extension/adapters/headers.ts:176-181](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L176-L181) ### Preview Data Constants and Symbols | Constant Name | Value / Identifier | Description | Sources | |---------------|-------------------|-------------|---------| | `COOKIE_NAME_PRERENDER_BYPASS` | `__prerender_bypass` | Cookie name used for preview mode bypass and draft mode ID storage. | [packages/next/src/server/api-utils/index.ts:111](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L111) | | `COOKIE_NAME_PRERENDER_DATA` | `__next_preview_data` | Cookie name used for encrypted preview data payloads. | [packages/next/src/server/api-utils/index.ts:112](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L112) | | `SYMBOL_PREVIEW_DATA` | `Symbol(__next_preview_data)` | Request property symbol used to cache resolved preview data. | [packages/next/src/server/api-utils/index.ts:116](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L116) | | `SYMBOL_CLEARED_COOKIES` | `Symbol(__prerender_bypass)` | Response property symbol indicating preview cookies have been cleared. | [packages/next/src/server/api-utils/index.ts:117](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L117) | | `PRERENDER_REVALIDATE_HEADER` | `x-matched-path` or revalidate header constant | Header key identifying on-demand revalidation requests. | [packages/next/src/server/api-utils/index.ts:7-8](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L7-L8) | Sources: [packages/next/src/server/api-utils/index.ts:7-8](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L7-L8), [packages/next/src/server/api-utils/index.ts:111-117](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/index.ts#L111-L117) ### Design Trade-Offs in Header Adaptation | Design Choice | Benefit | Cost | Sources | |---------------|---------|------|---------| | `HeadersAdapter` proxy wrapping over `IncomingHttpHeaders` | Allows case-insensitive header lookups matching web standard APIs without copying large header dictionaries. | Proxy trap overhead on every header access (`get`, `set`, `has`, `deleteProperty`). | [packages/next/src/server/web/spec-extension/adapters/headers.ts:36-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L36-L114) | | Request caching via `SYMBOL_PREVIEW_DATA` | Avoids repeated cookie parsing, JWT signature verification, and secret decryption within a single request lifecycle. | Ties cached preview state directly to the Node `IncomingMessage` request instance lifecycle. | [packages/next/src/server/api-utils/node/try-get-preview-data.ts:34-36](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/try-get-preview-data.ts#L34-L36), [packages/next/src/server/api-utils/node/try-get-preview-data.ts:111-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/try-get-preview-data.ts#L111-L114) | | Strict cookie parity checks (`previewModeId` vs `tokenPreviewData`) | Automatically purges invalid or half-set preview sessions to prevent state corruption. | Discards session data if either cookie is missing, causing unexpected logouts during partial cookie delivery. | [packages/next/src/server/api-utils/node/try-get-preview-data.ts:68-82](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/try-get-preview-data.ts#L68-L82) | Sources: [packages/next/src/server/api-utils/node/try-get-preview-data.ts:34-36](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/try-get-preview-data.ts#L34-L36), [packages/next/src/server/api-utils/node/try-get-preview-data.ts:68-82](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/try-get-preview-data.ts#L68-L82), [packages/next/src/server/api-utils/node/try-get-preview-data.ts:111-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/try-get-preview-data.ts#L111-L114), [packages/next/src/server/web/spec-extension/adapters/headers.ts:36-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L36-L114) > [!WARNING] > If an on-demand revalidation request is detected via `checkIsOnDemandRevalidate`, `tryGetPreviewData` immediately short-circuits and returns `false`, disabling preview mode for that request to prevent revalidation requests from executing under user preview contexts. Sources: [packages/next/src/server/api-utils/node/try-get-preview-data.ts:25-30](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/try-get-preview-data.ts#L25-L30) > [!CAUTION] > If only one of the two preview cookies (`COOKIE_NAME_PRERENDER_BYPASS` or `COOKIE_NAME_PRERENDER_DATA`) is present, `tryGetPreviewData` invokes `clearPreviewData(res)` unless `multiZoneDraftMode` is enabled, wiping both cookies from the response headers. Sources: [packages/next/src/server/api-utils/node/try-get-preview-data.ts:68-74](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/node/try-get-preview-data.ts#L68-L74) ## Static Export and Response Handling ### Overview During static generation or build-time export, Route Handlers (`app-route`) must be orchestrated to serialize their output bodies, metadata, and revalidation parameters into designated files via the multi-file writer. This orchestration coordinates request adaptation, module loading, static generation validation, execution error handling, and header serialization. Sources: [packages/next/src/export/routes/app-route.ts:36-182](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L36-L182) ### Call-Chain Execution Walkthrough The export orchestration for app route modules processes requests through a sequence of normalization, validation, and storage execution steps: `exportAppRoute()` → `module.ensureUserland()` → `isStaticGenEnabled()` → `module.handle()` → `afterRunner.executeAfter()` → `fileWriter.append()` 1. **`exportAppRoute()`**: Initializes the absolute request URL, wraps the node request using `NextRequestAdapter.fromNodeNextRequest`, and instantiates an `AfterRunner` and `AppRouteRouteHandlerContext`. Sources: [packages/next/src/export/routes/app-route.ts:58-98](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L58-L98) 2. **`module.ensureUserland()`**: Ensures that asynchronous modules (including those with top-level `await`) are fully resolved before the route handler is invoked. Sources: [packages/next/src/export/routes/app-route.ts:101-105](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L101-L105) 3. **`isStaticGenEnabled()`**: Inspects the loaded userland module to check if static generation is permitted, bypassing this check if the route is a metadata route or if `cacheComponents` is active. Sources: [packages/next/src/export/routes/app-route.ts:106-122](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L106-L122) 4. **`module.handle()`**: Dispatches the request through the handler pipeline, verifying that the returned value is a valid `Response` object and collecting revalidation tags and times into `renderOpts`. Sources: [packages/next/src/server/route-modules/app-route/module.ts:749-791](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L749-L791), [packages/next/src/export/routes/app-route.ts:124-124](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L124-L124) 5. **`afterRunner.executeAfter()`**: Executes any deferred callbacks registered during the route handler lifecycle prior to writing out final binary blobs and metadata. Sources: [packages/next/src/export/routes/app-route.ts:131-135](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L131-L135) 6. **`fileWriter.append()`**: Serializes the response body into a `_body` suffix file and writes response headers and status codes into a `_meta` suffix file. Sources: [packages/next/src/export/routes/app-route.ts:161-169](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L161-L169) ### Export Constants and File Suffixes | Constant / Identifier | Value | Purpose | Sources | |-----------------------|-------|---------|---------| | `ExportedAppRouteFiles.BODY` | `'BODY'` | Key representing the exported body file stream type. | [packages/next/src/export/routes/app-route.ts:31-34](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L31-L34) | | `ExportedAppRouteFiles.META` | `'META'` | Key representing the exported metadata file type. | [packages/next/src/export/routes/app-route.ts:31-34](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L31-L34) | | `NEXT_BODY_SUFFIX` | `'.body'` | File suffix extension appended when writing exported route bodies. | [packages/next/src/lib/constants.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/constants.ts), [packages/next/src/export/routes/app-route.ts:8-11](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L8-L11) | | `NEXT_META_SUFFIX` | `'.meta'` | File suffix extension appended when writing exported route headers and status metadata. | [packages/next/src/lib/constants.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/constants.ts), [packages/next/src/export/routes/app-route.ts:8-11](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L8-L11) | Sources: [packages/next/src/export/routes/app-route.ts:8-34](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L8-L34) > [!CAUTION] > If a route handler returns a response status code greater than or equal to 400 (except for status 404), `exportAppRoute` immediately intercepts the response and returns a revalidation value of `0`, preventing erroneous error pages from being cached as static output. Sources: [packages/next/src/export/routes/app-route.ts:126-129](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L126-L129) > [!WARNING] > Route Handlers enforce strict return verification: if a handler resolves without returning a valid instance of the web `Response` object, `AppRouteRouteModule.handle` throws an explicit error indicating that a `Response` or `NextResponse` must be returned across all execution branches. Sources: [packages/next/src/server/route-modules/app-route/module.ts:750-765](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L750-L765) ## Related - [[Server Request Lifecycle]] - [[Web Spec Adapters]] --- ## Technical docs: Metadata Generation URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-router-rendering/metadata-generation
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/lib/metadata/resolve-metadata.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolve-metadata.ts) - [packages/next/src/lib/metadata/metadata.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx) - [packages/next/src/lib/metadata/types/metadata-interface.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/metadata-interface.ts) - [packages/next/src/lib/metadata/get-metadata-route.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts) - [packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts) - [packages/next/src/lib/metadata/resolvers/resolve-basics.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-basics.ts) - [packages/next/src/lib/metadata/is-metadata-route.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/is-metadata-route.ts) - [packages/next/src/server/lib/router-utils/filesystem.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/filesystem.ts) - [packages/next/src/export/routes/app-page.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-page.ts) - [packages/next/src/lib/metadata/types/metadata-types.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/metadata-types.ts) - [packages/next/src/lib/metadata/resolvers/resolve-icons.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-icons.ts) - [packages/next/src/lib/metadata/resolvers/resolve-url.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-url.ts) - [packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/generate-metadata-access-prop-04.output.tsx](https://github.com/blade47/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/generate-metadata-access-prop-04.output.tsx) - [packages/next/src/lib/metadata/resolvers/resolve-title.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-title.ts) - [packages/next/src/lib/metadata/default-metadata.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/default-metadata.tsx) - [packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/generate-metadata-access-prop-03.output.tsx](https://github.com/blade47/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/generate-metadata-access-prop-03.output.tsx) - [packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/access-props-19.output.tsx](https://github.com/blade47/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/access-props-19.output.tsx) - [packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/generate-metadata-access-prop-04.input.tsx](https://github.com/blade47/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/generate-metadata-access-prop-04.input.tsx) - [packages/next/src/lib/metadata/types/icons.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/icons.ts) - [packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/generate-metadata-access-prop-01.output.tsx](https://github.com/blade47/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/generate-metadata-access-prop-01.output.tsx) - [packages/next/src/lib/metadata/metadata-context.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata-context.tsx) - [packages/next/src/shared/lib/router/utils/route-regex.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/route-regex.ts) - [packages/next/src/shared/lib/segment.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/segment.ts)
## Overview Metadata generation in Next.js provides a comprehensive, declarative system for defining, inheriting, and resolving document metadata and viewport configurations across the App Router hierarchy. By supporting both static metadata objects and dynamic asynchronous `generateMetadata` functions in Server Components, the system eliminates manual head-tag management while enforcing correct TypeScript contracts. It handles complex layout accumulation, default fallbacks, specialized social card processing, and URL resolution against `metadataBase`. Furthermore, it integrates tightly with the routing engine to identify special metadata files and compile route regular expressions, ultimately serializing processed metadata into React server head elements and static export artifacts. Sources: [packages/next/src/lib/metadata/resolve-metadata.ts:1-87](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolve-metadata.ts#L1-L87), [packages/next/src/lib/metadata/metadata.tsx:35-63](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L35-L63), [packages/next/src/lib/metadata/types/metadata-interface.ts:1-14](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/metadata-interface.ts#L1-L14), [packages/next/src/lib/metadata/get-metadata-route.ts:107-160](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L107-L160) ## Metadata API and Type Definitions ### Overview The Metadata API establishes public contracts and TypeScript interfaces for configuring document headers and viewport properties through static exports or dynamic asynchronous generation functions in Server Components. Next.js enforces strict separation: static `metadata` objects and `generateMetadata` functions are supported exclusively in Server Components, and routes must not export both a `metadata` object and a `generateMetadata` function from the same segment. Sources: [packages/next/src/lib/metadata/types/metadata-interface.ts:1-14](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/metadata-interface.ts#L1-L14) ### Asynchronous Metadata Generation Signatures Dynamic metadata uses `generateMetadata` functions that receive segment properties containing asynchronous promises for route parameters and search parameters. Because dynamic parameters are asynchronous in the App Router architecture, functions must await `props.params` or `props.searchParams` before accessing dynamic route segments. ```typescript type MetadataProps = { params: Promise<{ slug: string }> } export async function generateMetadata(props: MetadataProps) { return { title: (await props.params).slug, } } ``` Sources: [packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/generate-metadata-access-prop-03.output.tsx:1-10](https://github.com/blade47/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/generate-metadata-access-prop-03.output.tsx#L1-L10) > [!WARNING] > Do not export both a static `metadata` object and a dynamic `generateMetadata` function from the same route segment, as this creates an ambiguous resolution conflict. > Sources: [packages/next/src/lib/metadata/types/metadata-interface.ts:8-9](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/metadata-interface.ts#L8-L9) ### Core Interface Contracts The metadata subsystem defines structured types for standard HTML meta attributes, OpenGraph data, Twitter cards, alternative URLs, verification tokens, and viewport configurations. | Interface / Type | Description | Key Properties | Sources Reference | | --- | --- | --- | --- | | `Metadata` | Public configuration object for static exports | `title`, `description`, `applicationName`, `authors`, `generator`, `keywords`, `referrer`, `creator`, `publisher`, `robots`, `alternates`, `icons`, `openGraph`, `manifest`, `twitter`, `facebook`, `pinterest`, `verification`, `appleWebApp`, `formatDetection`, `itunes`, `abstract`, `appLinks`, `category`, `classification`, `other` | [packages/next/src/lib/metadata/types/metadata-interface.ts:540-564](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/metadata-interface.ts#L540-L564) | | `Viewport` | Viewport configuration contract | `width`, `height`, `initialScale`, `minimumScale`, `maximumScale`, `userScalable`, `viewportFit`, `interactiveWidget`, `themeColor`, `colorScheme` | [packages/next/src/lib/metadata/types/metadata-interface.ts:765-797](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/metadata-interface.ts#L765-L797) | | `Author` | Author structure for author metadata | `name`, `url` | [packages/next/src/lib/metadata/types/metadata-types.ts:38-43](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/metadata-types.ts#L38-L43) | | `IconDescriptor` | Structured icon descriptor | `url`, `type`, `sizes`, `color`, `rel`, `media`, `fetchPriority` | [packages/next/src/lib/metadata/types/metadata-types.ts:98-110](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/metadata-types.ts#L98-L110) | Sources: [packages/next/src/lib/metadata/types/metadata-interface.ts:540-564](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/metadata-interface.ts#L540-L564), [packages/next/src/lib/metadata/types/metadata-interface.ts:765-797](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/metadata-interface.ts#L765-L797), [packages/next/src/lib/metadata/types/metadata-types.ts:38-110](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/types/metadata-types.ts#L38-L110) ## Hierarchical Metadata Resolution Pipeline ### Overview The hierarchical metadata resolution pipeline processes layout-to-page tree accumulation, default fallback merging, and staged evaluation to generate final page-level configurations. The pipeline starts by initializing default baseline structures through dedicated factory helpers before traversing the component tree. ```typescript export function createDefaultViewport(): ResolvedViewport { return { width: 'device-width', initialScale: 1, themeColor: null, colorScheme: null, } } ``` Sources: [packages/next/src/lib/metadata/default-metadata.tsx:6-15](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/default-metadata.tsx#L6-L15) The base metadata structure initializes properties such as title, description, application name, and various structured metadata objects to `null`, establishing a consistent fallback base for accumulation across nested layouts. ```typescript export function createDefaultMetadata() { return { viewport: null, themeColor: null, colorScheme: null, metadataBase: null, title: null, description: null, applicationName: null, authors: null, generator: null, keywords: null, referrer: null, creator: null, publisher: null, robots: null, manifest: null, alternates: { canonical: null, languages: null, media: null, types: null }, icons: null, openGraph: null, twitter: null, verification: {}, appleWebApp: null, formatDetection: null, itunes: null, facebook: null, pinterest: null, abstract: null, appLinks: null, archives: null, assets: null, bookmarks: null, category: null, classification: null, pagination: { previous: null, next: null }, other: {}, } } ``` Sources: [packages/next/src/lib/metadata/default-metadata.tsx:17-65](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/default-metadata.tsx#L17-L65) ### Default Fallback Merging and Static File Integration Static file metadata resolution merges file-based fallback icons and manifest assets into target resolution objects. When static file metadata contains open graph or twitter images and current level metadata does not specify them, the pipeline resolves URLs and converts instances to string values. ```typescript async function mergeStaticMetadata( metadataBase: MetadataBaseURL, source: Metadata | null, target: any, staticFilesMetadata: StaticMetadata, metadataContext: MetadataContext, titleTemplates: TitleTemplates, leafSegmentStaticIcons: StaticIcons, pathname: Promise ) { if (!staticFilesMetadata) return target const { icon, apple, openGraph, twitter, manifest } = staticFilesMetadata if (icon) { leafSegmentStaticIcons.icon = icon } if (apple) { leafSegmentStaticIcons.apple = apple } if (twitter && !source?.twitter?.hasOwnProperty('images')) { const resolvedTwitter = resolveTwitter( { ...target.twitter, images: twitter } as Twitter, metadataBase, { ...metadataContext, isStaticMetadataRouteFile: true }, titleTemplates.twitter ) target.twitter = convertUrlsToStrings(resolvedTwitter) } if (openGraph && !source?.openGraph?.hasOwnProperty('images')) { const resolvedOpenGraph = await resolveOpenGraph( { ...target.openGraph, images: openGraph } as OpenGraph, metadataBase, pathname, { ...metadataContext, isStaticMetadataRouteFile: true }, titleTemplates.openGraph ) target.openGraph = convertUrlsToStrings(resolvedOpenGraph) } if (manifest) { target.manifest = manifest } return target } ``` Sources: [packages/next/src/lib/metadata/resolve-metadata.ts:159-208](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolve-metadata.ts#L159-L208) > [!NOTE] > `mergeStaticMetadata` updates the leaf segment static icons directly on the reference object while conditionally resolving missing image arrays for Twitter and OpenGraph contexts using static route file flags. > Sources: [packages/next/src/lib/metadata/resolve-metadata.ts:172-202](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolve-metadata.ts#L172-L202) ### Staged Evaluation and React Element Rendering Functions like `getResolvedMetadataImpl` and `getNotFoundMetadataImpl` channel requests into async render passes that generate markup nodes. ```typescript async function getResolvedMetadataImpl( tree: LoaderTree, pathname: Promise, searchParams: Promise, interpolatedParams: Params, metadataContext: MetadataContext, isRuntimePrefetchable: boolean, errorType?: MetadataErrorType | 'redirect' ): Promise { const errorConvention = errorType === 'redirect' ? undefined : errorType return renderMetadata( tree, pathname, searchParams, interpolatedParams, metadataContext, isRuntimePrefetchable, errorConvention ) } ``` Sources: [packages/next/src/lib/metadata/metadata.tsx:235-254](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L235-L254) > [!WARNING] > Viewport elements render a character set declaration followed by computed viewport attributes, color schemes, and media-query-bound theme colors, enforcing rigid ordering for mobile scaling parameters. > Sources: [packages/next/src/lib/metadata/metadata.tsx:354-418](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L354-L418) ## Specialized Resolvers and URL Handling ### Overview The metadata resolution subsystem transforms raw user-supplied metadata configurations into fully normalized, absolute URL-resolved output structures. This pipeline handles platform-specific social tags, title hierarchies, icons, and base URL resolution rules across different deployment environments. Sources: [packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts:161-207](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts#L161-L207), [packages/next/src/lib/metadata/resolvers/resolve-url.ts:35-55](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-url.ts#L35-L55) ### OpenGraph and Twitter Card Resolution Social metadata properties are processed by `resolveOpenGraph` and `resolveTwitter`, which inspect explicit object fields, validate images, and apply type-specific property constraints. OpenGraph property extraction relies on type definitions mapping types like `article`, `book`, `music.song`, and `video.movie` to their valid metadata fields. Sources: [packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts:145-189](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts#L145-L189) Twitter card resolution inspects the card type and configures subordinate structures such as `players` for the `player` card or `app` descriptors for the `app` card, defaulting to `summary_large_image` or `summary` depending on whether images are present. Sources: [packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts:237-256](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts#L237-L256) ### Title Templates and Metadata Base URLs Titles are resolved using `resolveTitle`, which evaluates string inputs against stashed template strings or processes object structures containing `default`, `absolute`, and `template` properties. When a template is active, `%s` placeholders are replaced with the target title value. Sources: [packages/next/src/lib/metadata/resolvers/resolve-title.ts:4-40](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-title.ts#L4-L40) URL resolution handles relative paths, absolute URLs, and environment-based fallbacks. `getSocialImageMetadataBaseFallback` inspects execution environments to determine appropriate base URLs: Sources: [packages/next/src/lib/metadata/resolvers/resolve-url.ts:35-55](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-url.ts#L35-L55) | Environment / Condition | Fallback Target | Source Reference | | :--- | :--- | :--- | | Development (`NODE_ENV === 'development'`) | Localhost (`http://localhost:3000` or custom port) | [packages/next/src/lib/metadata/resolvers/resolve-url.ts:10-15](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-url.ts#L10-L15) | | Vercel Preview (`NODE_ENV === 'production'` & `VERCEL_ENV === 'preview'`) | Preview Deployment URL (`VERCEL_BRANCH_URL` or `VERCEL_URL`) | [packages/next/src/lib/metadata/resolvers/resolve-url.ts:17-20](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-url.ts#L17-L20) | | Production Default | User-provided `metadataBase` $\rightarrow$ Vercel Production URL (`VERCEL_PROJECT_PRODUCTION_URL`) $\rightarrow$ Localhost | [packages/next/src/lib/metadata/resolvers/resolve-url.ts:22-25](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-url.ts#L22-L25) | > [!WARNING] > When no explicit `metadataBase` is set for relative social images in production, Next.js falls back to localhost or Vercel environment variables and emits a warning if Vercel system environment variables are not exposed. > Sources: [packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts:66-97](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-opengraph.ts#L66-L97) ### Icon Normalization Icons are resolved through `resolveIcons`, which processes input arrays, string URLs, or structured icon descriptor objects containing keys defined in `IconKeys`, ensuring `icon` and `apple` attachment fields are properly structured arrays. Sources: [packages/next/src/lib/metadata/resolvers/resolve-icons.ts:14-34](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-icons.ts#L14-L34) ```typescript export const resolveIcons: FieldResolver<'icons'> = (icons) => { if (!icons) { return null } const resolved: any = { icon: [], apple: [], } if (Array.isArray(icons)) { resolved.icon = icons.map(resolveIcon).filter(Boolean) } else if (isStringOrURL(icons)) { resolved.icon = [resolveIcon(icons)] } else { for (const key of IconKeys) { const values = resolveAsArrayOrUndefined(icons[key]) if (values) resolved[key] = values.map(resolveIcon) } } return resolved } ``` Sources: [packages/next/src/lib/metadata/resolvers/resolve-icons.ts:14-34](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/resolvers/resolve-icons.ts#L14-L34) ## Metadata Route Identification and Routing ### Overview Special metadata files such as `robots.txt`, `sitemap.xml`, `manifest.json`, `manifest.webmanifest`, favicons, and social images require specialized detection, extension mapping, and routing classification. Next.js identifies these files using pre-compiled regular expressions and fast-path heuristics, normalizing them into application routes or static metadata routes. Sources: [packages/next/src/lib/metadata/is-metadata-route.ts:6-162](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/is-metadata-route.ts#L6-L162), [packages/next/src/lib/metadata/get-metadata-route.ts:164-196](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L164-L196) ### Static Image Definitions and File Extensions The framework maintains explicit extension boundaries for static metadata image categories (`icon`, `apple`, `favicon`, `openGraph`, `twitter`) and metadata route extensions. Sources: [packages/next/src/lib/metadata/is-metadata-route.ts:6-31](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/is-metadata-route.ts#L6-L31) | Metadata Category | Filename Base | Supported Extensions | Sources Reference | | :--- | :--- | :--- | :--- | | `icon` | `icon` | `ico`, `jpg`, `jpeg`, `png`, `svg` | [packages/next/src/lib/metadata/is-metadata-route.ts:6-10](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/is-metadata-route.ts#L6-L10) | | `apple` | `apple-icon` | `jpg`, `jpeg`, `png` | [packages/next/src/lib/metadata/is-metadata-route.ts:11-14](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/is-metadata-route.ts#L11-L14) | | `favicon` | `favicon` | `ico` | [packages/next/src/lib/metadata/is-metadata-route.ts:15-18](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/is-metadata-route.ts#L15-L18) | | `openGraph` | `opengraph-image` | `jpg`, `jpeg`, `png`, `gif` | [packages/next/src/lib/metadata/is-metadata-route.ts:19-22](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/is-metadata-route.ts#L19-L22) | | `twitter` | `twitter-image` | `jpg`, `jpeg`, `png`, `gif` | [packages/next/src/lib/metadata/is-metadata-route.ts:23-26](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/is-metadata-route.ts#L23-L26) | Sources: [packages/next/src/lib/metadata/is-metadata-route.ts:6-27](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/is-metadata-route.ts#L6-L27) ### Fast-Path Matching and Compiled Regexes Path checking optimizes file routing via `fastPathCheck`, which evaluates exact path matches for `favicon.ico`, `robots.txt`, `manifest.json`, `manifest.webmanifest`, and `sitemap.xml` before falling back to full compiled regular expressions. Sources: [packages/next/src/lib/metadata/is-metadata-route.ts:60-95](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/is-metadata-route.ts#L60-L95) ```typescript const FAVICON_REGEX = /^[\\/]favicon\.ico$/ const ROBOTS_TXT_REGEX = /^[\\/]robots\.txt$/ const MANIFEST_JSON_REGEX = /^[\\/]manifest\.json$/ const MANIFEST_WEBMANIFEST_REGEX = /^[\\/]manifest\.webmanifest$/ const SITEMAP_XML_REGEX = /[\\/]sitemap\.xml$/ function fastPathCheck(normalizedPath: string): boolean | null { if (FAVICON_REGEX.test(normalizedPath)) return true if (ROBOTS_TXT_REGEX.test(normalizedPath)) return true if (MANIFEST_JSON_REGEX.test(normalizedPath)) return true if (MANIFEST_WEBMANIFEST_REGEX.test(normalizedPath)) return true if (SITEMAP_XML_REGEX.test(normalizedPath)) return true if ( !normalizedPath.includes('robots') && !normalizedPath.includes('manifest') && !normalizedPath.includes('sitemap') && !normalizedPath.includes('icon') && !normalizedPath.includes('apple-icon') && !normalizedPath.includes('opengraph-image') && !normalizedPath.includes('twitter-image') && !normalizedPath.includes('favicon') ) { return false } return null } ``` Sources: [packages/next/src/lib/metadata/is-metadata-route.ts:60-95](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/is-metadata-route.ts#L60-L95) > [!NOTE] > If a path fails exact fast-path matching and contains no metadata keywords, `fastPathCheck` immediately returns `false` to bypass regex compilation and testing. > Sources: [packages/next/src/lib/metadata/is-metadata-route.ts:80-95](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/is-metadata-route.ts#L80-L95) ### Route Normalization and Filesystem getItem Pipeline Metadata routes are normalized via `normalizeMetadataRoute`, mapping static file pages and dynamic route pages into their corresponding `/route` filesystem structures. Sources: [packages/next/src/lib/metadata/get-metadata-route.ts:171-196](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L171-L196) ```typescript export function normalizeMetadataRoute(page: string) { if (!isMetadataPage(page)) { return page } let route = page let suffix = '' if (page === '/robots') { route += '.txt' } else if (page === '/manifest') { route += '.webmanifest' } else { suffix = getMetadataRouteSuffix(page) } if (!route.endsWith('/route')) { const { dir, name: baseName, ext } = path.parse(route) route = path.posix.join( dir, `${baseName}${suffix ? `-${suffix}` : ''}${ext}`, 'route' ) } return route } ``` Sources: [packages/next/src/lib/metadata/get-metadata-route.ts:171-196](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L171-L196) During runtime filesystem routing in development (`opts.dev`), `getItem` intercepts metadata route files via `isMetadataRouteFile` and resolves them through `staticMetadataFiles`. Sources: [packages/next/src/server/lib/router-utils/filesystem.ts:515-525](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/filesystem.ts#L515-L525) ```typescript if (opts.dev && isMetadataRouteFile(itemPath, [], false)) { const fsPath = staticMetadataFiles.get(itemPath) if (fsPath) { return { type: 'nextStaticFolder', fsPath, itemPath: fsPath, } } } ``` Sources: [packages/next/src/server/lib/router-utils/filesystem.ts:515-525](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/filesystem.ts#L515-L525) > [!WARNING] > Sitemaps are explicitly excluded from suffix generation (`getMetadataRouteSuffix`) because each sitemap aggregates URLs across sub-routes, ensuring userland contains exactly one sitemap per pathname. > Sources: [packages/next/src/lib/metadata/get-metadata-route.ts:24-39](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L24-L39) ## Metadata Route Segment Normalization ### Overview Metadata route segments undergo normalization to handle group routes, parallel route folders, and dynamic parameters when generating physical route filenames and matching regular expressions. When a metadata file resides within a nested directory structure containing route groups `(group)` or parallel route slots `@slot`, a unique hash suffix is appended to prevent filename collisions. Sitemaps are excluded from this hashing behavior because they aggregate sub-route URLs and require a single canonical pathname. Route segment normalization also translates dynamic parameters into placeholder patterns for static prerendering and compiles named route regular expressions for runtime matching. Sources: [packages/next/src/lib/metadata/get-metadata-route.ts:31-52](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L31-L52), [packages/next/src/shared/lib/router/utils/route-regex.ts:378-403](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/route-regex.ts#L378-L403) ### Call-Chain Execution Walkthroughs Execution flows through explicit functional chains when handling metadata segment interpolation, route suffix resolution, and named regex compilation. 1. **Static Segment Normalization Chain (`fillMetadataSegment` → `fillStaticMetadataSegment` → `getStaticMetadataRoute` → `normalizeStaticMetadataRouteSegment`)**: `fillMetadataSegment` evaluates whether the segment is static or dynamic, delegating to `fillStaticMetadataSegment` [packages/next/src/lib/metadata/get-metadata-route.ts:141-160](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L141-L160), which resolves the base path using `getStaticMetadataRoute` [packages/next/src/lib/metadata/get-metadata-route.ts:90-101](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L90-L101) and cleans each segment through `normalizeStaticMetadataRouteSegment` [packages/next/src/lib/metadata/get-metadata-route.ts:62-73](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L62-73). ```typescript export function fillStaticMetadataSegment( segment: string, lastSegment: string ) { return normalizePathSep( path.join( getStaticMetadataRoute(segment), getMetadataRouteFilename(segment, lastSegment) ) ) } ``` Sources: [packages/next/src/lib/metadata/get-metadata-route.ts:90-101](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L90-L101) 2. **Route Suffix and Slot Detection Chain (`fillMetadataSegment` → `fillStaticMetadataSegment` → `getMetadataRouteFilename` → `getMetadataRouteSuffix` → `isParallelRouteSegment`)**: `fillMetadataSegment` calls `fillStaticMetadataSegment` [packages/next/src/lib/metadata/get-metadata-route.ts:141-160](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L141-L160), invoking `getMetadataRouteFilename` [packages/next/src/lib/metadata/get-metadata-route.ts:53-61](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L53-61), which calculates the route suffix via `getMetadataRouteSuffix` [packages/next/src/lib/metadata/get-metadata-route.ts:31-52](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L31-L52) and checks segment properties using `isParallelRouteSegment` [packages/next/src/shared/lib/segment.ts:11-14](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/segment.ts#L11-L14). ```typescript function getMetadataRouteSuffix(page: string) { const parentPathname = path.dirname(page) if (page.endsWith('/sitemap') || page.endsWith('/sitemap.xml')) { return '' } let suffix = '' const segments = parentPathname.split('/') if ( segments.some((seg) => isGroupSegment(seg) || isParallelRouteSegment(seg)) ) { suffix = djb2Hash(parentPathname).toString(36).slice(0, 6) } return suffix } ``` Sources: [packages/next/src/lib/metadata/get-metadata-route.ts:31-52](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L31-L52) 3. **Named Route Regex Compilation Chain**: `fillMetadataSegment` generates named regular expressions by calling `getNamedRouteRegex`, which delegates to `getNamedParametrizedRoute` and constructs minimal route keys via `buildGetSafeRouteKey`. Sources: [packages/next/src/lib/metadata/get-metadata-route.ts:141-160](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L141-L160), [packages/next/src/shared/lib/router/utils/route-regex.ts:177-190](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/route-regex.ts#L177-190), [packages/next/src/shared/lib/router/utils/route-regex.ts:378-403](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/route-regex.ts#L378-L403) ```typescript function buildGetSafeRouteKey() { let i = 0 return () => { let routeKey = '' let j = ++i while (j > 0) { routeKey += String.fromCharCode(97 + ((j - 1) % 26)) j = Math.floor((j - 1) / 26) } return routeKey } } ``` Sources: [packages/next/src/shared/lib/router/utils/route-regex.ts:177-190](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/route-regex.ts#L177-190) ```mermaid sequenceDiagram participant getMetadataRoute as get-metadata-route.ts participant segmentModule as segment.ts participant routeRegexModule as route-regex.ts getMetadataRoute->>getMetadataRoute: fillMetadataSegment() getMetadataRoute->>getMetadataRoute: fillStaticMetadataSegment() getMetadataRoute->>getMetadataRoute: getStaticMetadataRoute() getMetadataRoute->>getMetadataRoute: normalizeStaticMetadataRouteSegment() getMetadataRoute->>getMetadataRoute: getMetadataRouteFilename() getMetadataRoute->>getMetadataRoute: getMetadataRouteSuffix() getMetadataRoute->>segmentModule: isParallelRouteSegment() getMetadataRoute->>routeRegexModule: getNamedRouteRegex() routeRegexModule->>routeRegexModule: getNamedParametrizedRoute() routeRegexModule->>routeRegexModule: buildGetSafeRouteKey() ``` Sources: [packages/next/src/lib/metadata/get-metadata-route.ts:31-160](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L31-L160), [packages/next/src/shared/lib/router/utils/route-regex.ts:177-403](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/route-regex.ts#L177-403), [packages/next/src/shared/lib/segment.ts:11-14](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/segment.ts#L11-L14) ### Route Segment Reference Tables The normalization and regex compilation utilities rely on specific helper functions and helper patterns to parse routes. | Utility Function | Module Path | Purpose | Sources Reference | | :--- | :--- | :--- | :--- | | `getMetadataRouteSuffix` | `src/lib/metadata/get-metadata-route.ts` | Computes a 6-character base36 hash suffix using `djb2Hash` for parent paths containing route groups or parallel route segments. | [packages/next/src/lib/metadata/get-metadata-route.ts:31-52](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L31-L52) | | `normalizeStaticMetadataRouteSegment` | `src/lib/metadata/get-metadata-route.ts` | Iteratively replaces parameter patterns in static segments with `-` placeholders. | [packages/next/src/lib/metadata/get-metadata-route.ts:62-73](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L62-L73) | | `buildGetSafeRouteKey` | `src/shared/lib/router/utils/route-regex.ts` | Generates minimal lowercase alphabetical route keys (`a`, `b`, ..., `z`, `aa`) for regex named groups. | [packages/next/src/shared/lib/router/utils/route-regex.ts:177-190](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/route-regex.ts#L177-L190) | | `isGroupSegment` | `src/shared/lib/segment.ts` | Identifies route group folders enclosed in parentheses, such as `(post)`. | [packages/next/src/shared/lib/segment.ts:7-10](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/segment.ts#L7-L10) | | `isParallelRouteSegment` | `src/shared/lib/segment.ts` | Detects parallel route slots starting with `@` excluding `@children`. | [packages/next/src/shared/lib/segment.ts:11-14](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/segment.ts#L11-L14) | Sources: [packages/next/src/lib/metadata/get-metadata-route.ts:31-73](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L31-L73), [packages/next/src/shared/lib/router/utils/route-regex.ts:177-190](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/route-regex.ts#L177-190), [packages/next/src/shared/lib/segment.ts:7-14](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/segment.ts#L7-L14) > [!NOTE] > When `getMetadataRouteSuffix` evaluates a path, it checks if any segment in the parent path pathname satisfies `isGroupSegment` or `isParallelRouteSegment`. If true, it computes `djb2Hash(parentPathname).toString(36).slice(0, 6)`. > Sources: [packages/next/src/lib/metadata/get-metadata-route.ts:43-50](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L43-L50) ### Design Trade-Offs | Design Choice | Benefit | Cost | Sources Reference | | :--- | :--- | :--- | :--- | | **Hash-based Suffix for Group/Parallel Paths** | Prevents filename collisions when multiple metadata files map to the same output directory due to route grouping or parallel slots. | Adds non-deterministic or obscured hash suffixes (`-[0-9a-z]{6}`) to generated static asset filenames. | [packages/next/src/lib/metadata/get-metadata-route.ts:31-52](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L31-L52) | | **Sitemap Suffix Exclusion** | Ensures sitemaps aggregate sub-routes correctly without generating separate fragmented files per route group. | Risks path collisions if multiple userland sitemaps share an identical output pathname without proper separation. | [packages/next/src/lib/metadata/get-metadata-route.ts:24-39](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L24-L39) | | **Safe Route Key Generation (`a-z`)** | Keeps compiled named regex groups compact and avoids invalid JavaScript/RegExp identifier characters. | Requires internal translation tables (`routeKeys` and `reference.names`) to map back to original parameter keys. | [packages/next/src/shared/lib/router/utils/route-regex.ts:177-190](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/route-regex.ts#L177-L190) | Sources: [packages/next/src/lib/metadata/get-metadata-route.ts:24-52](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L24-L52), [packages/next/src/shared/lib/router/utils/route-regex.ts:177-190](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/route-regex.ts#L177-L190) > [!WARNING] > Invalid parameter keys (such as keys exceeding 30 characters or starting with a number) automatically fallback to `getSafeRouteKey()` during `getSafeKeyFromSegment` execution to preserve regular expression validity. > Sources: [packages/next/src/shared/lib/router/utils/route-regex.ts:220-229](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/route-regex.ts#L220-L229) ### Worked Example: Segment Filling and Route Regex Generation The following example demonstrates how `fillMetadataSegment` processes a dynamic route path versus a static metadata prerender path using the underlying signature functions. ```typescript import { fillMetadataSegment, getStaticMetadataPrerenderPathname } from './get-metadata-route' import { getNamedRouteRegex } from '../../shared/lib/router/utils/route-regex' // 1. Dynamic metadata segment filling with provided parameters const dynamicFilled = fillMetadataSegment( '/a/[slug]', { slug: 'b' }, 'open-graph', false ) // Result: '/a/b/open-graph' // 2. Static metadata prerender path conversion (replaces dynamic segments with '-') const staticPrerender = getStaticMetadataPrerenderPathname('/a/[slug]/opengraph-image.tsx') // Result: '/a/-/opengraph-image-[hash]' or similar normalized path // 3. Compiling a named route regex with route keys const routeRegexResult = getNamedRouteRegex('/a/[slug]', { prefixRouteKeys: false, includeSuffix: false, includePrefix: false, }) // Yields namedRegex '^/a/(?[^/]+?)(?:/)?$' and routeKeys { slug: 'slug' } ``` Sources: [packages/next/src/lib/metadata/get-metadata-route.ts:107-160](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/get-metadata-route.ts#L107-L160), [packages/next/src/shared/lib/router/utils/route-regex.ts:378-403](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/route-regex.ts#L378-L403) ## RSC Rendering and Export Integration ### Overview The conversion of resolved metadata into React server head elements and its integration with static export serialization relies on structured component creation and file writing utilities. The `createMetadataComponents` function generates three primary React components: `Viewport`, `Metadata`, and `MetadataOutlet`. These components orchestrate the rendering sequence for head tags, utilizing internal resolution pipelines like `resolveMetadata` and `resolveViewport`. Sources: [packages/next/src/lib/metadata/metadata.tsx:41-63](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L41-L63), [packages/next/src/lib/metadata/metadata.tsx:234-293](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L234-L293) ### Element Creation and Static Export Serialization The conversion process transitions resolved interface objects into native React element nodes through `createViewportElements` and `createMetadataElements`. For viewports, tags such as `meta[charset="utf-8"]`, viewport string interpolations, `theme-color`, and `color-scheme` are systematically appended to a tag array. For metadata, properties like title and description generate corresponding HTML elements. During static export operations handled by `app-page.ts`, the resulting page metadata, status, headers, and segment paths are compiled into a `RouteMetadata` structure and written out via `fileWriter.append()` with `NEXT_META_SUFFIX`. Sources: [packages/next/src/lib/metadata/metadata.tsx:354-448](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L354-L448), [packages/next/src/export/routes/app-page.ts:216-228](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-page.ts#L216-L228) > [!WARNING] > If a client-side rendering bailout or dynamic usage error occurs during static export generation, rendering fails unless trapped by specific error conventions like `isDynamicUsageError` or `isBailoutToCSRError`. > Sources: [packages/next/src/export/routes/app-page.ts:250-260](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-page.ts#L250-L260) ### Call-Chain Execution Walkthrough The metadata rendering pipeline executes in a structured sequence from asynchronous resolution to React node serialization: 1. `getResolvedMetadataImpl()` or `getNotFoundMetadataImpl()` receives the loader tree, pathname, search params, and context. 2. It invokes `renderMetadata()`. 3. `renderMetadata()` calls `resolveMetadata()`. 4. The resulting processed metadata object is passed into `createMetadataElements()`, returning a fragment of React elements. Sources: [packages/next/src/lib/metadata/metadata.tsx:235-331](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L235-L331) ### Metadata Rendering Components Reference | Component / Function | Input Parameters | Return Value | Sources Reference | | :--- | :--- | :--- | :--- | | `createMetadataComponents` | `tree`, `pathname`, `parsedQuery`, `metadataContext`, `interpolatedParams`, `errorType`, `serveStreamingMetadata`, `isRuntimePrefetchable` | `{ Viewport, Metadata, MetadataOutlet }` | [packages/next/src/lib/metadata/metadata.tsx:41-63](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L41-L63) | | `createViewportElements` | `viewport: ResolvedViewport` | `React.ReactElement[]` | [packages/next/src/lib/metadata/metadata.tsx:354-356](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L354-L356) | | `createMetadataElements` | `metadata: any` | `React.ReactElement[]` | [packages/next/src/lib/metadata/metadata.tsx:426-428](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L426-L428) | | `createMetadataContext` | `renderOpts` | `MetadataContext` | [packages/next/src/lib/metadata/metadata-context.tsx:4-11](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata-context.tsx#L4-L11) | Sources: [packages/next/src/lib/metadata/metadata.tsx:41-63](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L41-L63), [packages/next/src/lib/metadata/metadata.tsx:354-356](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L354-L356), [packages/next/src/lib/metadata/metadata.tsx:426-428](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata.tsx#L426-L428), [packages/next/src/lib/metadata/metadata-context.tsx:4-11](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/metadata/metadata-context.tsx#L4-L11) ## Related - [[App Server Rendering]] --- ## Technical docs: Instant Validation URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-router-rendering/instant-validation
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/app-render/instant-validation/instant-samples.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples.ts) - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/server/app-render/instant-validation/instant-validation.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx) - [packages/next/src/server/app-render/instant-validation/instant-samples-client.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples-client.ts) - [packages/next/src/server/app-render/instant-validation/instant-validation-error.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation-error.ts) - [packages/next/src/server/lib/router-utils/typegen.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/typegen.ts) - [packages/next/src/server/app-render/instant-validation/instant-config.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-config.tsx) - [packages/next/src/server/request/params.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts) - [packages/next/src/server/app-render/dynamic-rendering.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts) - [packages/next/src/server/request/search-params.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/search-params.ts) - [packages/next/src/server/typescript/rules/config.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/config.ts) - [packages/next/src/server/app-render/instant-validation/boundary-constants.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/boundary-constants.ts) - [packages/create-next-app/templates/app-api/ts/app/slug/route.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-api/ts/app/%5Bslug%5D/route.ts) - [packages/create-next-app/templates/app-api/js/app/slug/route.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-api/js/app/%5Bslug%5D/route.js) - [packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/route-access-prop-01.output.tsx](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/route-access-prop-01.output.tsx) - [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx) - [packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/route-access-prop-01.input.tsx](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/__testfixtures__/next-async-request-api-dynamic-props/route-access-prop-01.input.tsx) - [packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance-data.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance-data.ts) - [packages/create-next-app/templates/app-api/ts/app/route.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-api/ts/app/route.ts) - [packages/create-next-app/templates/app-api/js/app/route.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-api/js/app/route.js) - [packages/create-next-app/templates/default/ts/pages/api/hello.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/ts/pages/api/hello.ts) - [packages/next/src/client/components/client-page.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/client-page.tsx) - [packages/next/src/server/app-render/manifests-singleton.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/manifests-singleton.ts) - [packages/next/src/shared/lib/invariant-error.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/invariant-error.ts)
## Overview Instant validation provides a compile-time and development-time verification mechanism for Next.js App Router applications to ensure that routes configured with instant navigation or static optimization requirements satisfy their expected parameter contracts, layout constraints, and boundary structures. By evaluating loader trees, simulating request contexts with synthetic samples, and tracking dynamic data access across server and client component boundaries, the system catches misconfigurations and missing inputs early, preventing runtime rendering failures and static generation bailouts. Sources: [packages/next/src/server/app-render/instant-validation/instant-samples.ts:1-74](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples.ts#L1-L74), [packages/next/src/server/app-render/app-render.tsx:6533-6666](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L6533-L6666), [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:1-46](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L1-L46), [packages/next/src/server/app-render/instant-validation/instant-config.tsx:1-51](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L1-L51) ## Segment Configuration and Route Evaluation ### Overview Analyzing loader trees determines instant validation eligibility and blocking rules across segment configurations in Next.js. The system inspects layouts and pages recursively to evaluate validation levels, runtime prefetch capabilities, and whether specific segments are permitted to block navigation or require static shells. Sources: [packages/next/src/server/app-render/instant-validation/instant-config.tsx:53-109](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L53-L109) ### Segment Configuration Evaluation Workflow The loader tree evaluation engine follows a recursive traversal pattern across route segments, checking module exports and parallel route slots. The execution call chain operates through specific internal functions: 1. `anySegmentNeedsInstantValidation()` — Serves as the top-level orchestrator retrieving validation settings from the active `WorkStore`. 2. `visit()` — Recursively traverses the `LoaderTree` to extract layout or page modules via `getLayoutOrPageModule()`. 3. `isImplicitValidationSegment()` — Determines if unconfigured page or default segments qualify for implicit validation under non-manual default levels. 4. `isFrameworkErrorRoute()` — Evaluates whether a route corresponds to framework-synthesized error (`_not-found` or `_global-error`) entry points to exclude them from default validation. Sources: [packages/next/src/server/app-render/instant-validation/instant-config.tsx:28-51](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L28-L51), [packages/next/src/server/app-render/instant-validation/instant-config.tsx:126-224](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L126-L224) ### Validation Levels and Disable Flags Validation behavior is governed by configuration properties defined on segment configs and global work stores. The system maps validation options to internal execution flags and validation thresholds. | Configuration / Constant | Type | Meaning / Purpose | | :--- | :--- | :--- | | `VALIDATION_LEVEL.WARNING` | Enum (`0`) | Dev-time validation threshold used in `anySegmentNeedsInstantValidationInDev`. | | `VALIDATION_LEVEL.ERROR` | Enum (`1`) | Build-time validation threshold used in `anySegmentNeedsInstantValidationInBuild`. | | `unstable_disableValidation` | Boolean | Disables validation globally for the entire loader tree when encountered on any segment config. | | `unstable_disableDevValidation` | Boolean | Disables validation specifically during development runs when `level === VALIDATION_LEVEL.WARNING`. | | `unstable_disableBuildValidation` | Boolean | Disables validation specifically during build runs when `level === VALIDATION_LEVEL.ERROR`. | | `prefetch: 'allow-runtime'` | String | Indicates runtime prefetch configuration checked by `anySegmentHasRuntimePrefetchEnabled`. | Sources: [packages/next/src/server/app-render/instant-validation/instant-config.tsx:53-77](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L53-L77), [packages/next/src/server/app-render/instant-validation/instant-config.tsx:111-234](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L111-L234) > [!WARNING] > Setting `unstable_disableValidation: true` on any segment config short-circuits the recursive visitor and completely aborts instant validation for the entire route tree, ignoring all other explicit opt-ins or implicit rules. Sources: [packages/next/src/server/app-render/instant-validation/instant-config.tsx:168-193](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L168-L193) ### Sample Resolution and Precedence When resolving instant configuration samples for a page, inner segments override outer segments without performing merge logic. The `resolveInstantConfigSamplesForPage` function walks child trees along the `children` parallel route slot to collect sample definitions. | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | **Segment override without merging** | Predictable, isolated sample definitions per page or layout without deep object merging overhead. | Outer layout samples cannot be augmented by child pages; child definitions completely replace parent ones. | | **Cache scoped to WorkStore** | Avoids redundant loader tree walks during a single request or render context. | Cache lifetime is strictly bound to the duration of the active `WorkStore`. | | **Explicit framework error exclusion** | Prevents unintended validation failures on framework-managed error UI without user configuration. | Framework error routes require explicit user opt-in via `instant` configs if validation is desired. | Sources: [packages/next/src/server/app-render/instant-validation/instant-config.tsx:46-51](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L46-L51), [packages/next/src/server/app-render/instant-validation/instant-config.tsx:236-275](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-config.tsx#L236-L275) ## Synthetic Sample Generation and Tracking ### Overview Synthetic sample parameters, cookies, and headers are generated and tracked during instant validation to simulate runtime access conditions. When dynamic values like cookies or route parameters are accessed without being declared in the configured sample data, the subsystem logs and throws errors using specialized tracking mechanisms. Sources: [packages/next/src/server/app-render/instant-validation/instant-samples.ts:19-74](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples.ts#L19-L74) ### Sample Tracking and Error Execution The sample tracking infrastructure inspects the active `workUnitAsyncStorage` store to locate and record missing sample access errors into an `InstantValidationSampleTracking` container. The validation tracking call chain operates through specific internal functions: `getExpectedSampleTracking()` → retrieves the active store from `workUnitAsyncStorage` and branches on `workUnitStore.type` (accepting `'request'` or `'validation-client'`) → extracts `validationSampleTracking` or throws an `InvariantError` if missing → `trackMissingSampleError()` pushes the error to `missingSampleErrors` → `trackMissingSampleErrorAndThrow()` calls `trackMissingSampleError()` and then throws the `InstantValidationError`. > [!NOTE] > During validation store inspection, store types such as `'cache'`, `'prerender'`, and `'generate-static-params'` intentionally skip tracking retrieval, whereas any unhandled store type triggers a TypeScript exhaustive check (`workUnitStore satisfies never`). Sources: [packages/next/src/server/app-render/instant-validation/instant-samples.ts:19-74](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples.ts#L19-L74), [packages/next/src/server/app-render/instant-validation/instant-validation-error.ts:1-17](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation-error.ts#L1-L17) ### Cookie and Search Parameter Generation Cookies and search parameters are synthesized from sample configurations to construct proxy-wrapped request state. ```typescript export function createCookiesFromSample( sampleCookies: InstantSample['cookies'], route: string ): ReadonlyRequestCookies { const declaredNames = new Set() const cookies = new RequestCookies(new Headers()) if (sampleCookies) { for (const cookie of sampleCookies) { declaredNames.add(cookie.name) if (cookie.value !== null) { cookies.set(cookie.name, cookie.value) } } } const sealed = RequestCookiesAdapter.seal(cookies) return new Proxy(sealed, { get(target, prop, receiver) { if (prop === 'has') { const originalMethod = Reflect.get(target, prop, receiver) const wrappedMethod: typeof originalMethod = function (name) { if (!declaredNames.has(name)) { trackMissingSampleErrorAndThrow( createMissingCookieSampleError(route, name) ) } return originalMethod.call(target, name) } return wrappedMethod } if (prop === 'get') { // ... } } }) } ``` Sources: [packages/next/src/server/app-render/instant-validation/instant-samples.ts:81-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples.ts#L81-L114) Search parameters are parsed and built via `createURLSearchParamsFromSample`, iterating over configured entries and appending arrays or setting string values while ignoring `null` or `undefined` entries. Sources: [packages/next/src/server/app-render/instant-validation/instant-samples.ts:390-407](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples.ts#L390-L407) ### Route Parameter Interpolation Pathnames are generated from routes and sample parameters using `createPathnameFromRouteAndSampleParams`. The function splits the route by `/`, inspects each segment via `getSegmentParam`, and handles dynamic parameters, catch-all parameters, and static route segments. | Segment Param Type | Handling Behavior | Error or Fallback | | :--- | :--- | :--- | | `catchall` / `optional-catchall` | Looks up `params[param.paramName]`, encodes array values. | Uses `[rawSegment]` as placeholder if undefined; throws `InstantValidationError` if value is not an array. | | `dynamic` | Looks up `params[param.paramName]`, encodes string value. | Uses `rawSegment` as placeholder if undefined; throws `InstantValidationError` if value is not a string. | | Intercepting route variants (`catchall-intercepted-*`, `dynamic-intercepted-*`) | Unsupported interception route validation. | Throws `InvariantError` with message `'Not implemented: Validation of interception routes'`. | | Static segments | Appends raw segment directly to interpolated segments. | None. | Sources: [packages/next/src/server/app-render/instant-validation/instant-samples.ts:414-478](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples.ts#L414-L478) > [!CAUTION] > Interception route parameters encountered during sample pathname interpolation immediately throw an `InvariantError` because validation for interception routes is not implemented. Sources: [packages/next/src/server/app-render/instant-validation/instant-samples.ts:456-468](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples.ts#L456-L468) ### Root Parameter Assertion The `assertRootParamInSamples` function checks whether a root parameter is defined within sample parameters. If the parameter is missing, it constructs and throws an `InstantValidationError` via `trackMissingSampleErrorAndThrow`. Sources: [packages/next/src/server/app-render/instant-validation/instant-samples.ts:480-496](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples.ts#L480-L496) ## Client and Server Parameter Proxies ### Client and Server Parameter Proxies Parameter and search parameter validation bridges client and server component boundaries by inspecting active work unit stores and wrapping underlying parameter collections in exhaustive proxy objects. These proxies check property access against declared keys from `unstable_samples` configurations, intercepting undeclared lookups to trigger validation errors. Sources: [packages/next/src/server/app-render/instant-validation/instant-samples-client.ts:12-48](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples-client.ts#L12-L48), [packages/next/src/server/request/params.ts:644-657](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts#L644-L657) ### Client Parameter Instrumentation Walkchain When validating client components, parameter helpers inspect the current execution environment and construct specialized wrappers. The instrumentation process follows a distinct call chain: 1. `instrumentParamsForClientValidation()` queries `workAsyncStorage` and `workUnitAsyncStorage` to obtain active stores. 2. It evaluates `workUnitStore.type`, matching against the `'validation-client'` unit type. 3. If `validationSamples` exist, it extracts declared parameter keys using `Object.keys(workUnitStore.validationSamples.params ?? {})`. 4. It calls `createExhaustiveParamsProxy()` with the underlying parameters, declared keys set, and route path to return the restricted parameter proxy. Sources: [packages/next/src/server/app-render/instant-validation/instant-samples-client.ts:12-48](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples-client.ts#L12-L48) > [!NOTE] > If `workUnitStore` is not of type `'validation-client'` or contains no validation samples, `instrumentParamsForClientValidation` safely returns the unmodified `underlyingParams` reference without throwing. Sources: [packages/next/src/server/app-render/instant-validation/instant-samples-client.ts:17-47](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples-client.ts#L17-L47) ### Fallback Route Parameters and Search Params Client validation also verifies complete parameter coverage during specific expression evaluations. The helper function `expectCompleteParamsInClientValidation(expression)` checks fallback route parameters stored on validation client stores. | Validation Function | Target Store Type | Action on Missing Declarations | | :--- | :--- | :--- | | `expectCompleteParamsInClientValidation` | `validation-client` | Calls `trackMissingSampleErrorAndThrow` with an `InstantValidationError` detailing missing fallback parameters. | | `instrumentSearchParamsForClientValidation` | `validation-client` | Wraps `ReadonlyURLSearchParams` in an exhaustive search parameters proxy using declared sample keys. | | `createServerParamsProxyForInstantValidation` | `request` / `validation` | Intercepts server-side `params` access using `createExhaustiveParamsProxy`. | Sources: [packages/next/src/server/app-render/instant-validation/instant-samples-client.ts:50-87](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples-client.ts#L50-L87), [packages/next/src/server/app-render/instant-validation/instant-samples-client.ts:89-125](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-samples-client.ts#L89-L125), [packages/next/src/server/request/params.ts:644-657](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts#L644-L657) ## Validation Boundaries and Layout Markers ### Overview Client validation boundaries and slot markers manage validation tracking and scope attribution across component trees and parallel layout slots. The implementation uses context providers, specialized boundary components, and namespace objects to ensure rendered validation IDs are tracked and errors are correctly attributed to their originating configuration. Sources: [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:42-61](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L42-L61) ### Boundary Components and Tracking Call Chain Validation boundaries interact with asynchronous storage to record rendered boundaries and prevent server bundle contamination. The validation boundary execution follows a strict call chain: 1. `InstantValidationBoundary` (accessed via `NameSpace`) invokes `getValidationBoundaryTracking()` during render. 2. `getValidationBoundaryTracking()` retrieves the store from `workUnitAsyncStorage.getStore()`. 3. It checks `store.type`, expecting `'validation-client'`, and returns `store.boundaryState`. 4. The boundary component calls `state.renderedIds.add(id)` to register that the boundary with identifier `id` successfully rendered. Sources: [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:19-61](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L19-L61) > [!CAUTION] > Instant validation boundaries must never appear in browser bundles. Attempting to load `boundary-impl.tsx` when `typeof window !== 'undefined'` immediately throws an `InvariantError`. Sources: [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:13-17](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L13-L17) ### Slot Markers and Stack Resolution When a validation boundary spans multiple parallel slots, `SlotMarker` uses a cached dynamic component generator to render a marker matching `__next_instant_slot_N__`. During error handling, `resolveInstantStack` inspects the component stack using `slotMarkerRegex` to extract the slot index and retrieve the corresponding configuration stack. Sources: [packages/next/src/server/app-render/dynamic-rendering.ts:780-807](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/dynamic-rendering.ts#L780-L807), [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:111-138](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L111-L138) | Constant Name | Value | Purpose | | :--- | :--- | :--- | | `INSTANT_VALIDATION_BOUNDARY_NAME` | `__next_instant_validation_boundary__` | Component name identifier for instant validation boundaries in React stacks. | | `INSTANT_SLOT_MARKER_PREFIX` | `__next_instant_slot_` | Prefix string for parallel layout slot marker components. | | `INSTANT_SLOT_MARKER_SUFFIX` | `__` | Suffix string closing parallel layout slot marker components. | Sources: [packages/next/src/server/app-render/instant-validation/boundary-constants.ts:1-6](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/boundary-constants.ts#L1-L6) ### Boundary Placement Design Trade-Offs | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | Namespace object for boundary name | Retains exact function name at runtime despite production minification | Requires string slice trick (`.slice(0)`) to prevent bundler inlining | | Context-based placement (`PlaceValidationBoundaryBelowThisLevel`) | Automatically propagates boundary placement down layout trees without manual wrapping | Relies on router cooperation (`OuterLayoutRouter`) to render validation boundaries | | Cached slot marker generator (`slotMarkerCache`) | Avoids dynamic component creation overhead across re-renders | Retains references to generated marker components in memory for the process lifetime | Sources: [packages/next/src/server/app-render/instant-validation/boundary-impl.tsx:42-138](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/boundary-impl.tsx#L42-L138) ## Tree Depth Discovery and Serialization ### Overview Tree depth discovery and segment serialization manage the structural traversal of route hierarchies, transforming initial React Server Component (RSC) payloads into segment paths, route trees, and stage-specific chunks. Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:1-46](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L1-L46) ### Segment Path Discovery and Route Trees The segment validation planning phase relies on recursive tree traversal functions to map out `RouteTree` structures. `traverseRootSeedDataSegments` extracts root data from an `InitialRSCPayload` and delegates to `traverseCacheNodeSegments`, which processes segment nodes and parallel route children. ```typescript function traverseRootSeedDataSegments( initialRSCPayload: InitialRSCPayload, processSegment: ( segmentPath: SegmentPath, seedData: CacheNodeSeedData ) => void ) { const { flightRouterState, seedData } = getRootDataFromPayload(initialRSCPayload) const [rootSegment] = flightRouterState const rootPath = stringifySegment(rootSegment) return traverseCacheNodeSegments( rootPath, flightRouterState, seedData, processSegment ) } ``` Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:109-127](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L109-L127) Child segment paths are generated via `createChildSegmentPath`, which checks whether the parallel route key is `'children'` or a named parallel slot prefixed with `@`. ```typescript function createChildSegmentPath( parentPath: SegmentPath, parallelRouteKey: string, segment: Segment ): SegmentPath { const parallelRoutePrefix = parallelRouteKey === 'children' ? '' : `@${encodeURIComponent(parallelRouteKey)}/` return `${parentPath}/${parallelRoutePrefix}${stringifySegment(segment)}` as SegmentPath } ``` Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:170-180](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L170-L180) Segments are serialized into `SegmentPath` strings using `stringifySegment`, handling string segments by URI encoding them and array segments by encoding their components separated by pipe characters. ```typescript function stringifySegment(segment: Segment): SegmentPath { return ( typeof segment === 'string' ? encodeURIComponent(segment) : encodeURIComponent(segment[0]) + '|' + segment[1] + '|' + segment[2] ) as SegmentPath } ``` Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:182-188](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L182-L188) > [!NOTE] > If a segment key is a page segment (`__PAGE__`), search parameters may be appended. Consumers reading from the segment cache must ensure search parameters are correctly preserved and appended. Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:151-154](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L151-L154) ### Call-Chain Execution Walkthrough The execution path for data collection and module resolution follows a strict sequence of calls through the rendering and manifests infrastructure: 1. `collectStagedSegmentData` initializes the staged chunk streams and triggers processing of component modules and manifests. 2. `getServerModuleMap` is called during stream operations to resolve module identifiers against the global manifests singleton. 3. `getManifestsSingleton` retrieves the underlying manifest singleton from `globalThis`, throwing an `InvariantError` if it has not been initialized. ```mermaid sequenceDiagram participant collectStagedSegmentData as collectStagedSegmentData
(instant-validation.tsx) participant getServerModuleMap as getServerModuleMap
(manifests-singleton.ts) participant getManifestsSingleton as getManifestsSingleton
(manifests-singleton.ts) collectStagedSegmentData->>getServerModuleMap: Requests server module mappings getServerModuleMap->>getManifestsSingleton: Accesses manifests singleton store getManifestsSingleton-->>getServerModuleMap: Returns ManifestsSingleton record getServerModuleMap-->>collectStagedSegmentData: Returns ServerModuleMap proxy ``` Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:223-232](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L223-L232), [packages/next/src/server/app-render/manifests-singleton.ts:319-327](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/manifests-singleton.ts#L319-L327) ### Segment Stages and Types Reference | Export Name | Type / Values | Purpose | | :--- | :--- | :--- | | `SegmentPath` | `string & { _tag: 'SegmentPath' }` | Branded string type identifying a unique route segment path. | | `RouteTree` | Object (`path`, `segment`, `module`, `slots`) | Isomorphic structure to `FlightRouterState` augmented with instant configuration metadata. | | `SegmentStage` | `RenderStage.Static`, `RenderStage.Runtime`, `RenderStage.Dynamic` | Enumerated stages that route segments can traverse during rendering. | | `StageChunks` | `Record` | Mapping of render stages to accumulated binary chunk arrays. | | `StageEndTimes` | `RecordrefetchedSegmentStage, number>` | Timing records tracking when prefetched segment stages complete. | Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:88-107](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L88-L107), [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:194-210](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L194-L210) ### Serialization and Traversal Design Trade-Offs | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | Stringified segment paths (`stringifySegment`) | Provides unique, cache-friendly string keys for tree lookups | Requires URI encoding and delimiter joining overhead per segment node | | Global manifests singleton (`globalThis`) | Allows module-level server action and reference resolution without React context | Relies on global state mutation and explicit initialization order | | Parallel route slot prefixing (`@slot/`) | Distinguishes parallel route branches cleanly within unified path strings | Lengthens path strings and requires special parsing rules for child segments | Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:170-188](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L170-L188), [packages/next/src/server/app-render/manifests-singleton.ts:19-36](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/manifests-singleton.ts#L19-L36) ## Build and Dev Validation Lifecycle ### Overview The validation lifecycle manages the execution of asynchronous validation runs, processes validation errors, and handles diagnostics in development and build environments. Build-time validation relies on wrapper utilities that initialize custom contexts and execute sample-based renders. Sources: [packages/next/src/server/app-render/app-render.tsx:6533-6559](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L6533-L6559) ### Build Validation Call Chain The build-time validation sequence executes via a specific order of wrapper functions and context initializers: 1. `validateInstantConfigsInBuild` acts as the primary entry point, creating test log markers and delegating to `run()`. 2. `workAsyncStorage.exit` safely exits the outer work store scope before invoking `validateInstantConfigsInBuildImpl`. 3. `validateInstantConfigInBuildWithSample` initializes sample URLs, fallback parameters, and mock `WorkStore` and `AppRenderContext` structures. 4. `workAsyncStorage.run` executes the validation render within the isolated sample context. ```mermaid sequenceDiagram participant validateInstantConfigsInBuild as validateInstantConfigsInBuild
(app-render.tsx) participant workAsyncStorageExit as workAsyncStorage.exit
(app-render.tsx) participant validateInstantConfigsInBuildImpl as validateInstantConfigsInBuildImpl
(app-render.tsx) participant validateInstantConfigInBuildWithSample as validateInstantConfigInBuildWithSample
(app-render.tsx) validateInstantConfigsInBuild->>workAsyncStorageExit: Exits outer work store context workAsyncStorageExit->>validateInstantConfigsInBuildImpl: Invokes build implementation validateInstantConfigsInBuildImpl->>validateInstantConfigInBuildWithSample: Processes each sample item validateInstantConfigInBuildWithSample-->>validateInstantConfigsInBuildImpl: Returns validation results ``` Sources: [packages/next/src/server/app-render/app-render.tsx:6533-6593](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L6533-L6593), [packages/next/src/server/app-render/app-render.tsx:6668-6785](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L6668-L6785) ### InstantValidationError Properties and Handling The `InstantValidationError` class identifies exhaustive sample validation failures via a fixed string digest value. | Export Name | Type / Value | Purpose | | :--- | :--- | :--- | | `INSTANT_VALIDATION_ERROR_DIGEST` | `'INSTANT_VALIDATION_ERROR'` | Constant string assigned to error digests for identification. | | `isInstantValidationError` | `(err: unknown) => err is InstantValidationError` | Type guard verifying object type, `Error` instance, and matching digest. | | `InstantValidationError` | Class extending `Error` | Custom error subclass carrying the validation error digest. | Sources: [packages/next/src/server/app-render/instant-validation/instant-validation-error.ts:1-17](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation-error.ts#L1-L17) > [!WARNING] > If `success` evaluates to false during `validateInstantConfigsInBuild`, the logs record an error and throw a `StaticGenBailoutError` to immediately halt the static prerender process. Sources: [packages/next/src/server/app-render/app-render.tsx:6533-6559](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L6533-L6559) ## Related - [[Staged Dynamic Rendering]] - [[Navigation Boundaries]] --- ## Technical docs: Client App Router URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-routing/client-app-router
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/client/components/app-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx) - [packages/next/src/client/index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/index.tsx) - [packages/next/src/client/app-index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx) - [packages/next/src/client/components/layout-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/layout-router.tsx) - [packages/next/src/client/components/navigation.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts) - [packages/next/src/client/app-next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-next.ts) - [packages/next/src/shared/lib/router/router.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/router.ts) - [packages/next/src/client/components/app-router-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts) - [packages/next/src/client/app-next-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-next-turbopack.ts) - [packages/next/src/client/components/router-reducer/ppr-navigations.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts) - [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx) - [packages/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/main/packages/next-devtools/userspace/app/segment-explorer-node.tsx) - [packages/next/src/server/app-render/instant-validation/instant-validation.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx) - [packages/next/src/client/app-next-dev.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-next-dev.ts) - [packages/next/src/client/app-globals.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-globals.ts) - [packages/next/src/next-devtools/dev-overlay.browser.tsx](https://github.com/blade47/next.js/blob/main/packages/next-devtools/dev-overlay.browser.tsx) - [packages/next/src/client/next-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-turbopack.ts) - [packages/next/src/client/page-bootstrap.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/page-bootstrap.ts) - [packages/next/src/shared/lib/app-router-context.shared-runtime.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/app-router-context.shared-runtime.ts) - [packages/next/src/server/app-render/entry-base.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/entry-base.ts) - [packages/next/src/server/app-render/instant-validation/instant-config.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-config.tsx) - [packages/next/src/client/app-bootstrap.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-bootstrap.ts) - [packages/next/src/client/next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next.ts) - [packages/next/src/client/components/use-action-queue.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/use-action-queue.ts) - [packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-app-router/app/layout.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-19-installed-pure-app-router/app/layout.ts) - [packages/next/src/client/next-dev-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-dev-turbopack.ts) - [packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-app-router/app/layout.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/__testfixtures__/react-18-installed-pure-app-router/app/layout.ts) - [packages/next/src/client/components/client-page.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/client-page.tsx) - [packages/next/src/client/components/client-segment.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/client-segment.tsx) - [packages/next/src/server/request/params.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts)
## Overview The Client App Router subsystem serves as the core client-side orchestration engine for Next.js App Router applications. It manages the lifecycle of React Server Component (RSC) payload ingestion, hydration, segment tree reconciliation, browser history synchronization, and navigation actions. By decoupling router state transitions from React component trees via an external action queue and leveraging React's concurrent features, the Client App Router enables partial prerendering (PPR), segment caching, instant navigations, and resilient state preservation across layout boundaries. The architecture addresses the fundamental challenge of rendering and updating nested server components on the client without forcing full-page reloads. It coordinates between client-side navigation APIs (`useRouter`, `usePathname`, `useSearchParams`), layout nesting (`LayoutRouter`), and server-driven React Server DOM (Flight) stream decoding. Through fine-grained cache nodes and back-forward cache (bfcache) management, it ensures that shared layouts retain stable identities and internal states while leaf pages transition smoothly. ```mermaid flowchart TD Bootstrap["appBootstrap() / initialize()"] --> Stream["Read Flight Stream (ReadableStream)"] Stream --> Payload["Initial RSC Payload Decoded"] Payload --> AppRouter["AppRouter Component ()"] AppRouter --> ActionQueue["Mutable Action Queue & useActionQueue"] ActionQueue --> LayoutRouter["Nested LayoutRouters ()"] LayoutRouter --> History["HistoryUpdater & window.history synchronization"] ``` Sources: [packages/next/src/client/app-index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L184-L274), [packages/next/src/client/components/app-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx#L59-L112) --- ## Initialization and Hydration Pipeline The client runtime initializes through entry points such as `app-next.ts` or `app-next-turbopack.ts`, which invoke `appBootstrap` and `hydrate`. Before any React components mount, `appBootstrap` executes any pending scripts in sequence (such as inline polyfills or `beforeInteractive` scripts). Concurrently, the Flight data stream is established via `__next_f` buffered chunks or direct stream reads. ```mermaid sequenceDiagram participant Bootstrap as appBootstrap participant Stream as ReadableStream participant Payload as createFromReadableStream participant Hydrate as hydrate() participant Router as AppRouter Bootstrap->>Stream: Register writer & consume __next_f chunks Stream->>Payload: Pipe stream into Flight decoder Payload-->>Hydrate: Resolve initialServerResponse (InitialRSCPayload) Hydrate->>Router: Mount with action queue & initial state ``` Sources: [packages/next/src/client/app-bootstrap.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-bootstrap.ts#L58-L81), [packages/next/src/client/app-index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L184-L274), [packages/next/src/client/app-next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-next.ts#L10-L18) > [!NOTE] > When cache components and experimental cached navigations are enabled, the initial Flight stream is teed using `readable.tee()` so that a clone can be truncated at static stage boundaries for caching. Sources: [packages/next/src/client/app-index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/app-index.tsx#L194-L206) --- ## State Management and Action Queue State within the Client App Router does not live directly inside standard React `useState` hooks at the root level; instead, it resides in a mutable action queue created by `createMutableActionQueue`. The `useActionQueue` hook bridges this external mutable state with React by maintaining a canonical state via `React.useState` and wrapping it with `useOptimistic` to support gesture transitions and pending navigations. When an action is dispatched (via `dispatchAppRouterAction` or `dispatchNavigateAction`), it flows through the action queue. In development mode, `nextDispatch` wraps the action with the development rendering indicator to visually reflect server renders and route transitions. ```typescript export function createMutableActionQueue( initialState: AppRouterState, instrumentationHooks: ClientInstrumentationHooks | null ): AppRouterActionQueue { const actionQueue: AppRouterActionQueue = { state: initialState, dispatch: (payload: ReducerActions, setState: DispatchStatePromise) => dispatchAction(actionQueue, payload, setState), action: async (state: AppRouterState, action: ReducerActions) => { const result = reducer(state, action) return result }, pending: null, last: null, onRouterTransitionStart: instrumentationHooks !== null && typeof instrumentationHooks.onRouterTransitionStart === 'function' ? instrumentationHooks.onRouterTransitionStart : null, } return actionQueue } ``` Sources: [packages/next/src/client/components/app-router-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L220-L240), [packages/next/src/client/components/use-action-queue.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/use-action-queue.ts#L61-L101) > [!WARNING] > Dispatched actions before router initialization throw an internal error: `Internal Next.js error: Router action dispatched before initialization.` Ensure all components interacting with `dispatchAppRouterAction` mount strictly inside the `` tree. Sources: [packages/next/src/client/components/use-action-queue.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/use-action-queue.ts#L33-L40) --- ## Layout Routers and Segment Rendering Layout and page segments are orchestrated hierarchically via `LayoutRouter` components (`InnerLayoutRouter` and `OuterLayoutRouter`). At each level of the route tree, the router renders the active segment alongside historical segments preserved within hidden React `` boundaries to enable instant back/forward transitions and bfcache restoration. The `LayoutRouterContext` supplies downstream components with contextual boundaries, parent parameters, active cache nodes, and bfcache identifiers (`bfcacheIdNumber`), formatted with a `b` prefix (e.g. `_r_0_`) to mirror React's `useId()` and prevent collisions when concatenating keys. ```typescript export function useRouter(): AppRouterInstance { const router = useContext(AppRouterContext) if (router === null) { throw new Error('invariant expected app router to be mounted') } const layout = useContext(LayoutRouterContext) const bfcacheIdNumber = layout?.parentCacheNode.bfcacheId ?? 0 return useMemo( () => ({ back: router.back, forward: router.forward, refresh: router.refresh, // ... push, replace, prefetch, bfcacheId bfcacheId: String(bfcacheIdNumber), }), [router, bfcacheIdNumber] ) } ``` Sources: [packages/next/src/client/components/navigation.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts#L176-L200), [packages/next/src/client/components/layout-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/layout-router.tsx#L678-L690) --- ## Browser History Synchronization The `HistoryUpdater` component, embedded within ``, uses a `useInsertionEffect` to synchronize the app router's internal state with `window.history`. It captures the current Flight router tree and rendered search parameters, builds an `AppHistoryState` object, and injects it into `window.history.state` under the `__PRIVATE_NEXTJS_INTERNALS_TREE` property with the `__NA: true` identifier flag. ```mermaid flowchart LR State["appRouterState changed"] --> Insertion["useInsertionEffect in HistoryUpdater"] Insertion --> Check{"pushRef.pendingPush && href !== canonicalUrl?"} Check -- Yes --> Push["window.history.pushState() & clear pendingPush"] Check -- No --> Replace["window.history.replaceState()"] Push --> Commit["setLastCommittedTree(tree)"] Replace --> Commit Commit --> Ping["pingVisibleLinks() for prefetch re-validation"] ``` Sources: [packages/next/src/client/components/app-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx#L59-L112) > [!IMPORTANT] > The history state distinguishes Next.js App Router entries from Pages Router entries and external history states via the `__NA: true` property. If `__NA` is absent, app-router history restoration handlers ignore the popstate event. Sources: [packages/next/src/client/components/app-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx#L79-L86) --- ## Public Navigation API and Actions The public router instance (`publicAppRouterInstance`) provides methods exposed to client components through `useRouter()`. All navigation mutations (`push`, `replace`, `refresh`) wrap their execution in `startTransition` to integrate with React's concurrent rendering model. | Method | Signature | Behavior | | :--- | :--- | :--- | | `push` | `(href: string, options?: NavigateOptions) => void` | Navigates to `href`, pushing a new history entry and scrolling by default. | | `replace` | `(href: string, options?: NavigateOptions) => void` | Navigates to `href`, replacing the current history entry. | | `refresh` | `() => void` | Dispatches `ACTION_REFRESH` to fetch fresh server data for the current route tree. | | `prefetch` | `(href: string, options?: PrefetchOptions) => void` | Prefetches `href` into the Segment Cache using PPR or Full strategies. | | `back` | `() => void` | Invokes `window.history.back()`. | | `forward` | `() => void` | Invokes `window.history.forward()`. | | `hmrRefresh` | `() => void` | Resets known routes and triggers an HMR refresh (development only). | Sources: [packages/next/src/client/components/app-router-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L391-L501), [packages/next/src/shared/lib/app-router-context.shared-runtime.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/app-router-context.shared-runtime.ts#L33-L90) --- ## Design Trade-Offs | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | **External Action Queue** (`createMutableActionQueue`) | Decouples router state transitions from React component lifecycles, enabling async prefetching and non-blocking dispatches. | Requires manual synchronization (`useActionQueue`, `useOptimistic`) to bridge external state into React renders. | | **Segment Cache & Bfcache Activity** | Preserves component state across navigations for instant back/forward transitions. | Higher client memory consumption due to retained DOM nodes and cached Flight trees. | | **Asynchronous Params Unwrapping** | Prevents synchronous blocking during server-side rendering and static shell generation. | Requires developers to unwrap `params` via `await` or `React.use()` in client and server components. | Sources: [packages/next/src/client/components/app-router-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L220-L256), [packages/next/src/client/components/layout-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/layout-router.tsx#L683-L690), [packages/next/src/server/request/params.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/params.ts#L871-L881) ## Related - [[Router State Reducer]] - [[Client Segment Cache]] --- ## Technical docs: Router State Reducer URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-routing/router-state-reducer
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts) - [packages/next/src/client/components/router-reducer/ppr-navigations.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts) - [packages/next/src/client/components/router-reducer/router-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts) - [packages/next/src/client/components/segment-cache/navigation.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts) - [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts) - [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts) - [packages/next/src/client/components/router-reducer/router-reducer-types.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts) - [packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts) - [packages/next/src/client/components/app-router-instance.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts) - [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts) - [packages/next/src/client/components/segment-cache/scheduler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts) - [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts) - [packages/next/src/client/components/router-reducer/create-initial-router-state.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts) - [packages/next/src/client/components/segment-cache/cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts) - [packages/next/src/client/components/segment-cache/optimistic-routes.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts) - [packages/next/src/client/components/app-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx) - [packages/next/src/next-devtools/dev-overlay/shared.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/shared.ts) - [packages/next/src/client/components/router-reducer/compute-changed-path.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/compute-changed-path.ts) - [packages/next/src/client/components/use-action-queue.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/use-action-queue.ts)
## Overview The router state reducer manages asynchronous client-side navigations, state patches, and history traversals for the Next.js App Router by maintaining a centralized action queue and dispatch switchboard. It processes incoming actions—such as client navigations, server-driven patches, page refreshes, hot-module reloads, server actions, and history restorations—to update the global application state, coordinate segment cache invalidations, and synchronize browser history entries. By decoupling the router state from React and coordinating transitions through specialized reducer modules, the system ensures reliable tree reconciliation, scroll and focus management, and seamless partial prerendering (PPR) support across user interactions. Sources: [packages/next/src/client/components/router-reducer/router-reducer.ts:23-58](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L23-L58), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:203-250](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L203-L250), [packages/next/src/client/components/app-router-instance.ts:95-144](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L95-L144) ## Action Dispatch Architecture and Queue The Action Dispatch Architecture handles incoming navigation intents, history restorations, and data mutations by routing them through `useActionQueue` and scheduling them in an `AppRouterActionQueue`. Because the app router state lives outside React, actions are queued sequentially and dispatched to the main `clientReducer` switchboard, which delegates to specialized reducers based on action types. Sources: [packages/next/src/client/components/app-router-instance.ts:44-144](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L44-L144), [packages/next/src/client/components/router-reducer/router-reducer.ts:23-58](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L23-L58), [packages/next/src/client/components/use-action-queue.ts:12-16](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/use-action-queue.ts#L12-L16) Actions flow through a sequence of functions that manage asynchronous execution and queue priority. When an action is dispatched, `dispatchAction()` evaluates the current queue state: 1. `dispatchAction()` creates a deferred promise for asynchronous actions (unless the action type is `ACTION_RESTORE`) and constructs an `ActionQueueNode`. 2. If `actionQueue.pending` is `null`, the action runs immediately via `runAction()`. 3. If an action is already pending and the incoming payload is an `ACTION_NAVIGATE` or `ACTION_RESTORE`, the current pending action is marked as `discarded = true`, its `.next` pointer is preserved, and the navigation starts immediately via `runAction()`. 4. Other action types are appended to `actionQueue.last` and scheduled for execution after preceding actions finish. 5. `runAction()` executes `actionQueue.action(prevState, payload)`, handling promises and invoking `handleResult()` or `runRemainingActions()`. 6. `runRemainingActions()` advances `actionQueue.pending` to the next node in the queue and triggers the next action, or checks if `actionQueue.needsRefresh` is set to dispatch an `ACTION_REFRESH`. Sources: [packages/next/src/client/components/app-router-instance.ts:71-215](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L71-L215) > [!WARNING] > Navigations and restore actions (`ACTION_NAVIGATE` or `ACTION_RESTORE`) take immediate precedence over pending background actions by setting `actionQueue.pending.discarded = true`. Discarded actions that revalidated data will automatically trigger a deferred refresh via `actionQueue.needsRefresh` once remaining actions complete. Sources: [packages/next/src/client/components/app-router-instance.ts:113-127](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L113-L127), [packages/next/src/client/components/app-router-instance.ts:191-198](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router-instance.ts#L191-L198) The `clientReducer` function acts as the central router switchboard, matching incoming `action.type` strings against known action constants and delegating to specialized reducer functions. If environment checks or unknown action types are encountered, specific branches or errors are thrown. Sources: [packages/next/src/client/components/router-reducer/router-reducer.ts:23-58](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L23-L58) | Action Constant | Switch Case Handler | Target Module / Behavior | Sources | | :--- | :--- | :--- | :--- | | `ACTION_NAVIGATE` | `navigateReducer(state, action)` | Handles client-side navigation tasks and prefetch resolution. | [packages/next/src/client/components/router-reducer/router-reducer.ts:28-30](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L28-L30) | | `ACTION_SERVER_PATCH` | `serverPatchReducer(state, action)` | Applies server-driven Flight router state patches. | [packages/next/src/client/components/router-reducer/router-reducer.ts:31-33](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L31-L33) | | `ACTION_RESTORE` | `restoreReducer(state, action)` | Restores route states during history traversal (`popstate`). | [packages/next/src/client/components/router-reducer/router-reducer.ts:34-36](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L34-L36) | | `ACTION_REFRESH` | `refreshReducer(state, action)` | Refreshes the current route and revalidates data segments. | [packages/next/src/client/components/router-reducer/router-reducer.ts:37-39](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L37-L39) | | `ACTION_HMR_REFRESH` | `hmrRefreshReducer(state)` | Development-only HMR refresh; throws an error in production. | [packages/next/src/client/components/router-reducer/router-reducer.ts:40-50](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L40-L50) | | `ACTION_SERVER_ACTION` | `serverActionReducer(state, action)` | Executes server actions and parses resulting Flight patches. | [packages/next/src/client/components/router-reducer/router-reducer.ts:51-53](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L51-L53) | Sources: [packages/next/src/client/components/router-reducer/router-reducer.ts:23-58](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L23-L58) > [!NOTE] > On the server side, `reducer` evaluates to `serverReducer`, which is a noop function that immediately returns the incoming state unchanged, enabling better tree-shaking for server-side bundles. Sources: [packages/next/src/client/components/router-reducer/router-reducer.ts:60-70](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer.ts#L60-L70) ## Initial Router State Construction Client-side router state initialization begins with `createInitialRouterState`, which processes the `InitialRSCPayload` delivered during server-side rendering or initial document load. This function extracts payload fields such as canonical URL parts, Flight data, rendered search queries, prefetch streams, and dynamic stale times to construct the initial `AppRouterState`. Sources: [packages/next/src/client/components/router-reducer/create-initial-router-state.ts:25-55](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts#L25-L55) ```mermaid graph TD A[InitialRSCPayload] --> B[Extract Flight Data & Tree] B --> C[convertRootFlightRouterStateToRouteTree] C --> D[createInitialCacheNodeForHydration] D --> E[Cache Seeding & Route Discovery] E --> F[Return AppRouterState] ``` Sources: [packages/next/src/client/components/router-reducer/create-initial-router-state.ts:32-100](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts#L32-L100) The construction pipeline proceeds through a series of deterministic steps to establish the initial route tree and cache node structure: 1. `createInitialRouterState()` destructures `InitialRSCPayload` and normalizes the initial canonical URL and Flight data parts via `getFlightDataPartsFromPath`. 2. `convertRootFlightRouterStateToRouteTree()` converts the initial `FlightRouterState` into a `RouteTree`, tracking metadata vary paths. 3. `createInitialCacheNodeForHydration()` builds the initial cache node hierarchy using the route tree, seed data, head, and computed dynamic stale time. 4. `discoverKnownRoute()` is invoked if running in the browser with a valid metadata vary path, registering the route pattern for future navigation prediction. Sources: [packages/next/src/client/components/router-reducer/create-initial-router-state.ts:38-118](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts#L38-L118) > [!NOTE] > For statically generated HTML pages, the `FlightRouterState` baked into the initial RSC payload may omit correct segment inlining hints. The server marks these trees with `InliningHintsStale`, causing the route cache entry to expire immediately so that subsequent prefetches fetch correct hints from the `/_tree` endpoint. Sources: [packages/next/src/client/components/router-reducer/create-initial-router-state.ts:78-83](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts#L78-L83) When running in the browser (`location !== null`), the initialization routine populates the segment cache depending on whether the page is partially or fully static: - **Partially static pages:** If `initialStaticStageByteLength` and `initialFlightStreamForCache` are available, the Flight stream is cloned, truncated at the static stage byte boundary, decoded via `decodeStageUntilBoundary`, and cached using `writePrerenderResponseIntoCache` with `FetchStrategy.PPR`. - **Fully static pages:** If seed data and stale times are present without a partial byte boundary, the entire decoded seed data is written directly into the cache via `writePrerenderResponseIntoCache`, and the unused stream is cancelled. - **Runtime prefetch streams:** If `initialRuntimePrefetchStream` is present, `processRuntimePrefetchStream` decodes the stream and writes runtime data into the cache under `FetchStrategy.PPRRuntime`. Sources: [packages/next/src/client/components/router-reducer/create-initial-router-state.ts:126-227](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts#L126-L227) > [!WARNING] > The initial hydration payload is treated as a complete, self-sufficient snapshot for rendering the page. The router deliberately avoids fetching missing data during initialization to preserve a reliable recovery path via full document reloads. Sources: [packages/next/src/client/components/router-reducer/create-initial-router-state.ts:230-244](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/create-initial-router-state.ts#L230-L244) ## Navigation Reducer and Tree Transitions Client navigation processing begins inside `navigateReducer`, which acts as the entry point for handling `NavigateAction` payloads from user interactions or programmatic navigation calls. The reducer performs early validation checks—intercepting external URLs and page redirect meta tags to trigger hard Multi-Page Application (MPA) navigations via `completeHardNavigation`—before delegating internal routing tasks to segment cache navigation handlers. Sources: [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts:23-41](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L23-L41), [packages/next/src/client/components/segment-cache/navigation.ts:620-653](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L620-L653) ```mermaid sequenceDiagram participant Action as NavigateAction participant Reducer as navigateReducer participant CacheNav as navigateUsingSegmentCache participant SoftNav as completeSoftNavigation Action->>Reducer: Dispatch navigation action Reducer->>Reducer: Check external URL / redirect meta alt External or Redirect Reducer->>CacheNav: completeHardNavigation() else Internal Navigation Reducer->>CacheNav: navigate(...) CacheNav->>SoftNav: completeSoftNavigation() end ``` Sources: [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts:23-56](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L23-L56), [packages/next/src/client/components/segment-cache/navigation.ts:600-618](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L600-L618) Internal navigations processed through `navigateUsingSegmentCache` resolve prefetch trees, construct target route states, and coordinate Partial Prerendering (PPR) tasks. Once target cache nodes and `FlightRouterState` trees are computed, the navigation concludes by invoking completion routines. - **Call Chain:** `navigateReducer()` → `navigateUsingSegmentCache()` (located in segment-cache navigation) → `completeSoftNavigation()` → constructs final `AppRouterState`. - **Soft Navigation Completion:** `completeSoftNavigation` evaluates path changes for interception routes via `computeChangedPath`, detects hash-only URL modifications, computes scroll targets, and manages scroll reference invalidation across pending navigations. Sources: [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts:23-56](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L23-L56), [packages/next/src/client/components/segment-cache/navigation.ts:600-618](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L600-L618), [packages/next/src/client/components/segment-cache/navigation.ts:655-791](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L655-L791) > [!NOTE] > During soft navigations, if a user opts out of scrolling (`scroll={false}`), any newly created per-node scroll reference is neutralized by setting `scrollRef.current = false`, while prior active scroll references carried forward on cache nodes remain intact. Sources: [packages/next/src/client/components/segment-cache/navigation.ts:714-725](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L714-L725) When building route trees for navigation, abstract route patterns are translated into concrete instances through `reifyRouteTree`, which substitutes dynamic segment values from resolved parameters and computes vary paths to key segment cache entries correctly. Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:879-885](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L879-L885) ```typescript function reifyRouteTree( pattern: RouteTree, resolvedParams: ResolvedParams, search: NormalizedSearch, parentPartialVaryPath: PartialSegmentVaryPath | null, acc: ReifyAccumulator ): RouteTree ``` Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:879-885](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L879-L885) Parallel slots and page segments are traversed recursively. For page nodes, vary paths incorporate request keys and search parameters, whereas layout segments finalize without search parameters. Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:924-982](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L924-L982) | Navigation Behavior / Constant | Value / Type | Purpose in Navigation Reducer | Sources | | :--- | :--- | :--- | :--- | | `ScrollBehavior.NoScroll` | Enum value | Disables automatic scrolling for the current navigation action. | [packages/next/src/client/components/segment-cache/navigation.ts:714-725](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L714-L725) | | `FreshnessPolicy.Default` | Enum value | Controls cache lookup freshness and fallback behavior during segment retrieval. | [packages/next/src/client/components/segment-cache/navigation.ts:606](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L606) | | `DYNAMIC_STALETIME_MS` | Number (ms) | Dynamic segment staleness duration derived from experimental environment configurations. | [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts:16-18](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L16-L18) | | `STATIC_STALETIME_MS` | Number (ms) | Static segment staleness duration computed via segment cache settings. | [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts:19-21](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L19-L21) | Sources: [packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts:16-21](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts#L16-L21), [packages/next/src/client/components/segment-cache/navigation.ts:606](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L606), [packages/next/src/client/components/segment-cache/navigation.ts:714-725](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L714-L725) > [!CAUTION] > Javascript URLs (`javascript:`) passed to navigation actions are explicitly blocked and logged as security errors inside `completeHardNavigation`, immediately returning the unmodified router state. Sources: [packages/next/src/client/components/segment-cache/navigation.ts:625-630](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L625-L630) ## Server Patch and Tree Reconciliation When a route mismatch occurs or server-driven updates are received, the client router applies Flight router state patches to update active route subtrees and cache nodes. This reconciliation process is managed by `serverPatchReducer`, which validates whether the incoming server response matches the expected router state before executing a known route navigation with a refresh freshness policy. Sources: [packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts:16-69](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts#L16-L69) The server patch reconciliation workflow delegates execution through specific functions depending on whether payload validation succeeds. The execution sequence follows: `serverPatchReducer()` → checks `action.mpa` / `action.seed` → validates `action.previousTree === state.tree` → `navigateToKnownRoute()` (or falls back to `completeHardNavigation()` or `refreshReducer()`). Sources: [packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts:28-68](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts#L28-L68) During tree traversal and reconciliation, utility functions inspect `FlightRouterState` structures to extract paths and parameters. The path extraction and tree differencing call-chain operates as: `computeChangedPath()` → `computeChangedPathImpl()` → matches segments via `matchSegment()` → falls back to `extractPathFromFlightRouterState()` and `normalizeSegments()`. Sources: [packages/next/src/client/components/router-reducer/compute-changed-path.ts:208-220](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/compute-changed-path.ts#L208-L220) > [!NOTE] > If a more recent navigation has occurred since the mismatched patch was dispatched (`action.previousTree !== state.tree`), `serverPatchReducer` aborts the retry and invokes `refreshReducer` to evict stale dynamic data while preserving the latest navigation state. Sources: [packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts:35-40](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.ts#L35-L40) The router state reducer handles several distinct action types governing server patches, refreshes, and navigation restoration, defined in the reducer type definitions. Sources: [packages/next/src/client/components/router-reducer/router-reducer-types.ts:6-128](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L6-L128) | Action Constant / Interface | Type Value | Purpose in Router State Reducer | Sources | | :--- | :--- | :--- | :--- | | `ACTION_REFRESH` | `'refresh'` | Triggers a full page data refresh, fetching fresh Flight data and updating the root cache and router state. | [packages/next/src/client/components/router-reducer/router-reducer-types.ts:6](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L6), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:24-27](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L24-L27) | | `ACTION_NAVIGATE` | `'navigate'` | Initiates client-side navigation (`push` or `replace`) using prefetched data or dynamic fetch requests. | [packages/next/src/client/components/router-reducer/router-reducer-types.ts:7](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L7), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:59-87](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L59-L87) | | `ACTION_RESTORE` | `'restore'` | Applies a known router state from browser history states during `popstate` events. | [packages/next/src/client/components/router-reducer/router-reducer-types.ts:8](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L8), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:98-105](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L98-L105) | | `ACTION_SERVER_PATCH` | `'server-patch'` | Applies provided Flight data and router tree patches back into the active client cache. | [packages/next/src/client/components/router-reducer/router-reducer-types.ts:9](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L9), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:117-128](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L117-L128) | | `ACTION_HMR_REFRESH` | `'hmr-refresh'` | Handles hot-module reload refresh triggers within the router reducer. | [packages/next/src/client/components/router-reducer/router-reducer-types.ts:10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L10), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:38-40](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L38-L40) | | `ACTION_SERVER_ACTION` | `'server-action'` | Dispatches server action requests, parsing responses and handling revalidations and redirects. | [packages/next/src/client/components/router-reducer/router-reducer-types.ts:11](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L11), [packages/next/src/client/components/router-reducer/router-reducer-types.ts:49-56](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L49-L56) | Sources: [packages/next/src/client/components/router-reducer/router-reducer-types.ts:6-128](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/router-reducer-types.ts#L6-L128) ## Refresh and HMR Reducers Standard and hot-module reload (HMR) refreshes re-fetch dynamic RSC payload data for the current URL while coordinating segment cache invalidation. When a refresh action or an HMR update occurs, the reducer invalidates stale dynamic entries to ensure fresh content is displayed without throwing away the underlying route structure. Sources: [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:23-39](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L23-L39), [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts:8-10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts#L8-L10) The refresh workflow processes standard refreshes and HMR refreshes through a dedicated sequence of helper functions. The call-chain executes as follows: `refreshReducer()` / `hmrRefreshReducer()` → `refreshDynamicData()` → `invalidateBfCache()` → `hasInterceptionRouteInCurrentTree()` → `convertServerPatchToFullTree()` → `navigateToKnownRoute()`. Sources: [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:19-104](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L19-L104), [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts:8-10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts#L8-L10) 1. **Cache Invalidation Check:** `refreshReducer` checks whether testing flags bypass cache invalidation (`process.env.__NEXT_EXPOSE_TESTING_API && action.bypassCacheInvalidation`). If not bypassed, it invokes `invalidateSegmentCacheEntries(currentNextUrl, currentRouterState)`. 2. **Dynamic Data Refresh:** Both `refreshReducer` and `hmrRefreshReducer` delegate to `refreshDynamicData(state, freshnessPolicy)`, passing either `FreshnessPolicy.RefreshAll` or `FreshnessPolicy.HMRRefresh`. 3. **BFCache & Interception Resolution:** `refreshDynamicData` clears the back/forward cache via `invalidateBfCache()`, then resolves `nextUrlForRefresh` by evaluating `hasInterceptionRouteInCurrentTree(state.tree)`. 4. **Seed Conversion and Navigation:** A `refreshSeed` is generated by calling `convertServerPatchToFullTree()`, and the refresh is finalized by invoking `navigateToKnownRoute()` with a `'replace'` navigation type and `ScrollBehavior.NoScroll`. Sources: [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:31-103](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L31-L103), [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts:8-10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts#L8-L10) > [!NOTE] > During a refresh, the router invalidates the segment cache (which holds dynamic RSC data) but deliberately leaves the route cache intact, because the underlying route tree structure does not change across a refresh. Sources: [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:23-27](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L23-L27) | Function / Parameter | Type / Value | Purpose in Refresh Subsystem | Sources | | :--- | :--- | :--- | :--- | | `refreshReducer` | Function | Entry point for standard user or programmatic refreshes, invalidating segment cache and triggering dynamic data re-fetch. | [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:19-39](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L19-L39) | | `hmrRefreshReducer` | Function | Entry point for Hot Module Replacement refreshes, invoking dynamic data re-fetch with an HMR policy. | [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts:8-10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts#L8-L10) | | `FreshnessPolicy.RefreshAll` | Enum member | Policy value instructing navigation handlers to refresh all dynamic data. | [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:38-43](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L38-L43) | | `FreshnessPolicy.HMRRefresh` | Enum member | Policy value indicating an HMR-triggered refresh across route segments. | [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts:8-9](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts#L8-L9) | | `ScrollBehavior.NoScroll` | Enum member | Scroll preservation setting ensuring page scroll position remains unchanged during a refresh. | [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:63](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L63) | Sources: [packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts:1-104](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts#L1-L104), [packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts:1-10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/hmr-refresh-reducer.ts#L1-L10) ## Server Action Execution and Revalidation Server action execution and revalidation in the router reducer handles processing server action requests, parsing action Flight responses, resolving redirects, and updating cache state. When a server action is invoked, the action reducer extracts server reference info, encodes reply arguments, builds action headers including the router state tree, and executes the fetch request. Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:104-121](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L104-L121) The processing of a server action flows through a specific sequence of operations: 1. `fetchServerAction()` parses and encodes action arguments using `extractInfoFromServerReferenceId()`, `omitUnusedArgs()`, and `encodeReply()`. 2. The returned promise resolves in the reducer handler, checking `revalidationKind`. If revalidation occurs (`ActionDidRevalidateStaticAndDynamic`), `invalidateBfCache()` and `invalidateEntirePrefetchCache()` are invoked, followed by `startRevalidationCooldown()`. 3. If a redirect location is present, `isExternalURL()` determines whether to trigger an external MPA hard navigation via `completeHardNavigation()` or an internal SPA redirect with `createRedirectErrorForAction()`. 4. When new Flight data and rendered search results are returned (`flightData !== undefined && flightDataRenderedSearch !== undefined`), `convertServerPatchToFullTree()` generates a redirect seed, `discoverKnownRoute()` registers the route pattern, and `navigateToKnownRoute()` completes the transition. Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:109-522](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L109-L522) > [!WARNING] > If a server action triggers a redirect without sending any Flight data, the router treats it as an external redirect and immediately forces a hard navigation via `completeHardNavigation()`. Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:423-429](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L423-L429) | Constant / Function | Type / Value | Purpose in Server Action Subsystem | Sources | | :--- | :--- | :--- | :--- | | `FetchServerActionResult` | Type Definition | Encapsulates redirect location, redirect type, revalidation kind, action result, flight data, search metadata, and interception flags. | [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:93-102](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L93-L102) | | `ActionDidNotRevalidate` | Revalidation Kind | Indicates the server action performed no revalidation. | [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:64](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L64) | | `ActionDidRevalidateDynamicOnly` | Revalidation Kind | Indicates revalidation affected only dynamic segments. | [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:65](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L65) | | `ActionDidRevalidateStaticAndDynamic` | Revalidation Kind | Indicates revalidation affected both static and dynamic cache entries, triggering entire prefetch cache invalidation. | [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:66](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L66), [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:361-363](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L361-L363) | | `startRevalidationCooldown` | Function | Initiates a cooldown period before re-prefetching to allow CDN cache propagation. | [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:51](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L51), [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:367](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L367) | Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:51-102](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L51-L102), [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:343-368](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L343-L368) ## History Traversal and Restore Reducer History traversal relies on the `restoreReducer` function to handle `popstate` events, reconstruct the target route state from history entries, and coordinate back/forward cache integration. When a user triggers browser navigation, the reducer inspects the incoming `RestoreAction` history state. If the history state lacks a valid `FlightRouterState`—such as for pre-hydration entries or anchor link hash navigations—it retains the existing tree via `state.tree` to prevent invalid router states. Otherwise, it extracts the restore tree, rendered search parameters, and canonical URL. Sources: [packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts:22-42](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts#L22-L42) The restoration process follows an explicit execution sequence from state extraction through task spawning and tree traversal: 1. `restoreReducer()` — Receives `state` and `RestoreAction`, resolving `treeToRestore` from `historyState.tree` or falling back to `state.tree`. 2. `convertServerPatchToFullTree()` — Takes the restored tree, current timestamp, and unknown dynamic stale times (`UnknownDynamicStaleTime`) to build a full `NavigationSeed` containing the route tree and vary paths. 3. `startPPRNavigation()` — Evaluates the navigation task using `FreshnessPolicy.HistoryTraversal`, evaluating the segment cache against the restore seed route tree. 4. Branch check (`task === null`) — If the task creation fails, it falls back to a hard navigation via `completeHardNavigation(state, restoredUrl, 'replace')`. Otherwise, it proceeds to spawn dynamic requests. 5. `spawnDynamicRequests()` — Dispatches background requests for dynamic data using the `'replace'` navigate type and history traversal freshness policy. 6. `completeTraverseNavigation()` — Finalizes the traversal update, returning a new `AppRouterState` with `preserveCustomHistoryState` set to `true`. Sources: [packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts:22-104](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts#L22-L104), [packages/next/src/client/components/segment-cache/navigation.ts:803-822](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L803-L822) > [!WARNING] > History traversal never uses route prediction. If a dynamic data mismatch occurs during a restore task, the retry handler must traverse the known route tree to locate and mark the mismatched entry. Sources: [packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts:82-92](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/restore-reducer.ts#L82-L92) When restoring state, helper utilities inspect router trees to determine pathnames and parameters. `extractPathFromFlightRouterState()` processes segment nodes, ignoring default segment keys and interception markers, while `computeChangedPath()` calculates differences between state trees during traversals. Sources: [packages/next/src/client/components/router-reducer/compute-changed-path.ts:81-118](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/compute-changed-path.ts#L81-L118), [packages/next/src/client/components/router-reducer/compute-changed-path.ts:208-220](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/compute-changed-path.ts#L208-L220) ## Related - [[Client App Router]] - [[Client Segment Cache]] --- ## Technical docs: Client Segment Cache URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-routing/client-segment-cache
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/client/components/segment-cache/cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts) - [packages/next/src/client/components/segment-cache/scheduler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts) - [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts) - [packages/next/src/server/app-render/collect-segment-data.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/collect-segment-data.tsx) - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts) - [packages/next/src/client/components/router-reducer/ppr-navigations.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts) - [packages/next/src/server/app-render/instant-validation/instant-validation.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx) - [packages/next/src/client/components/segment-cache/lru.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts) - [packages/next/src/client/components/segment-cache/cache-map.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts) - [packages/next/src/client/components/layout-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/layout-router.tsx) - [packages/next/src/server/dev/on-demand-entry-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts) - [packages/next/src/client/components/segment-cache/bfcache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts) - [packages/next/src/client/components/segment-cache/vary-path.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts) - [packages/next/src/server/lib/incremental-cache/memory-cache.external.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/memory-cache.external.ts) - [packages/next/src/client/components/links.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts) - [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts)
## Overview The Client Segment Cache is a specialized client-side data management system in Next.js designed to store and serve pre-fetched React Server Component (RSC) route trees and individual page segments efficiently. Its primary role in the broader system is to accelerate client-side transitions and support Partial Prerendering (PPR) by maintaining granular cache entries that can be selectively queried, composed, and updated without blocking navigation. It solves the performance and bandwidth problems of traditional full-page prefetches by breaking down page responses into hierarchical segment units and matching them against dynamic request parameters. Key design decisions include synchronous cache lookups using multi-key paths, bounded memory consumption enforced by an LRU eviction strategy, and prioritized background prefetch scheduling. The segment cache integrates closely with adjacent components such as link visibility observers, router reducers, server action revalidations, and back-forward cache management to synchronize client navigation states with server-rendered updates. Sources: [packages/next/src/client/components/segment-cache/cache.ts:1-137](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L1-L137), [packages/next/src/client/components/segment-cache/scheduler.ts:62-166](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L62-L166), [packages/next/src/client/components/segment-cache/cache-map.ts:1-94](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L1-L94), [packages/next/src/client/components/segment-cache/lru.ts:1-54](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L1-L54), [packages/next/src/client/components/segment-cache/vary-path.ts:1-50](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L1-L50) ## Cache Architecture and Map Storage The cache architecture relies on specialized multi-key map data structures and strict synchronous access patterns. Most asynchronous operations in the prefetch cache avoid `async/await` and instead spawn subtasks that write results to cache entries, attaching ping listeners to notify the prefetch queue. This allows synchronous traversal of data structures and immediate snapshots of the cache during synchronous updates, avoiding race conditions in a mutable cache. Sources: [packages/next/src/client/components/segment-cache/cache.ts:124-135](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L124-L135) The underlying storage mechanism is a specialized multi-key map where keys are tuples called keypaths. Each element of a keypath represents an input contributing to the entry value, such as a URL and parameters listed by the Vary header. The cache map supports a special `Fallback` key: when an exact match for a keypath is absent, the cache checks for a Fallback match. Because values exist at only a single keypath at a time, successive lookups are optimized by caching the internal map entry directly on the value via its `ref` field, skipping $O(n^2)$ fallback traversals. Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:5-54](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L5-L54) Values stored in the map must implement the `MapValue` protocol, tracking references, size, expiration timestamps, cache versions, and entry statuses. Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:87-93](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L87-L93) | Protocol Property | Type | Meaning | Sources | | :--- | :--- | :--- | :--- | | `ref` | `UnknownMapEntry \| null` | Direct pointer back to the containing map node for fast lookups. | [packages/next/src/client/components/segment-cache/cache-map.ts:88](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L88) | | `size` | `number` | Memory size of the cache entry in bytes for LRU tracking. | [packages/next/src/client/components/segment-cache/cache-map.ts:89](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L89) | | `staleAt` | `number` | Absolute timestamp in milliseconds when the entry becomes stale. | [packages/next/src/client/components/segment-cache/cache-map.ts:90](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L90) | | `version` | `number` | Cache version number used for global cache invalidation checks. | [packages/next/src/client/components/segment-cache/cache-map.ts:91](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L91) | | `status` | `EntryStatus` | Lifecycle phase of the entry (Empty, Pending, Fulfilled, or Rejected). | [packages/next/src/client/components/segment-cache/cache-map.ts:92](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L92) | Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:87-93](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L87-L93) Entry statuses are tracked via the `EntryStatus` enumeration. Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:76-81](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L76-L81) | Status Constant | Numeric Value | Lifecycle Meaning | Sources | | :--- | :--- | :--- | :--- | | `EntryStatus.Empty` | `0` | No data present; detached or placeholder entry. | [packages/next/src/client/components/segment-cache/cache-map.ts:77](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L77) | | `EntryStatus.Pending` | `1` | Request dispatched; waiting for server data. | [packages/next/src/client/components/segment-cache/cache-map.ts:78](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L78) | | `EntryStatus.Fulfilled` | `2` | Data successfully received and parsed. | [packages/next/src/client/components/segment-cache/cache-map.ts:79](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L79) | | `EntryStatus.Rejected` | `3` | Request failed with an error response. | [packages/next/src/client/components/segment-cache/cache-map.ts:80](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L80) | Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:76-81](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L76-L81) > [!NOTE] > Each element of a keypath may have a `Fallback`, making cache retrieval an $O(n^2)$ operation in the worst case, though keypaths are expected to remain short. Values cannot be stored at multiple keypaths simultaneously; overlapping cases must be expressed using `Fallback` keys. Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:28-48](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L28-L48) Retrieving items from the cache map involves recursive matching with fallback handling and lazy expiration checks. The call sequence for reading an entry is `getFromCacheMap()` → `getEntryWithFallbackImpl()` → `lazilyEvictIfNeeded()` → `isValueExpired()`. Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:229-288](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L229-L288) 1. `getFromCacheMap()` initiates the lookup by passing parameters to `getEntryWithFallbackImpl()`. If a valid entry is found, it updates LRU positioning via `lruPut()` and returns `entry.value`. Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:229-258](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L229-L258) 2. `getEntryWithFallbackImpl()` traverses keypath elements recursively. For each level, it checks `map.get(key)` for an exact match. If no exact match exists, it falls back to `map.get(Fallback)`. Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:300-370](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L300-L370) 3. When reaching the terminal node, `lazilyEvictIfNeeded()` invokes `isValueExpired()`. If `value.staleAt <= now` or `value.version < currentCacheVersion`, `deleteMapEntry()` evicts the entry immediately and returns `null`. Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:260-288](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L260-L288) When writing values, `setInCacheMap()` executes `getOrInitialize()` to locate or build the keypath node, invokes `setMapEntryValue()` to re-link references and update LRU sizes, and calls `lruPut()` to promote the entry to the front of the LRU list. Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:374-389](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L374-L389) | Design Choice | Benefit | Cost | Sources | | :--- | :--- | :--- | :--- | | **Tuple Keypaths with Fallback** | Allows partial parameter matching and route template reuse without full re-fetches. | Increases lookup complexity up to $O(n^2)$ when multiple fallback levels are evaluated. | [packages/next/src/client/components/segment-cache/cache-map.ts:28-33](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L28-L33) | | **Direct Value `ref` Caching** | Bypasses recursive tree traversal on subsequent accesses to the same entry. | Requires maintaining bidirectional pointers between map entries and values during re-assignments. | [packages/next/src/client/components/segment-cache/cache-map.ts:49-54](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L49-L54) | | **Lazy Expiration Checks** | Avoids expensive background sweeping timers by validating stamps on read. | Expired entries linger in memory until accessed or evicted by LRU capacity limits. | [packages/next/src/client/components/segment-cache/cache-map.ts:268-288](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L268-L288) | Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:28-54](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L28-L54), [packages/next/src/client/components/segment-cache/cache-map.ts:268-288](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L268-L288) > [!WARNING] > During navigation lookups using `readSegmentCacheEntryForNavigation`, the cache performs up to two lookups: first an `onlyMatchFulfilled` pass that skips Pending or Rejected entries at more specific keypaths to find a cached shell fallback, followed by a regular fallback lookup if no fulfilled entry is found. Sources: [packages/next/src/client/components/segment-cache/cache.ts:503-527](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L503-L527) ## Vary Paths and Parameter Resolution Vary paths represent linked lists of parameters and structural identifiers that govern how cache entries are reused across distinct URL states. Each vary path node specifies an `id` (such as a path parameter name or `'?'` for search parameters), a concrete or wildcard `value`, an optional `isRootParam` boolean indicator, and a `parent` pointer. Because route matching requires strict positional consistency, vary paths are constructed as pure functions of a segment's position within a route tree and the post-rewrite query URL. Sources: [packages/next/src/client/components/segment-cache/vary-path.ts:12-49](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L12-L49) The client segment cache defines distinct vary path structures depending on whether a query targets an entire route, a layout segment, or a page segment. Route vary paths chain a pathname, a search string, and an optional Next-URL header. Segment vary paths bind a segment request key with nested parent path parameters or rendered search parameters. Sources: [packages/next/src/client/components/segment-cache/vary-path.ts:56-97](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L56-L97) When a server response fulfills a segment or route request, vary paths are re-keyed to reflect exact parameter dependencies reported by the server or derived from interception rules. Unused parameters are replaced with the `Fallback` constant, allowing entries to serve subsequent requests with different parameter values. Sources: [packages/next/src/client/components/segment-cache/vary-path.ts:125-148](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L125-L148), [packages/next/src/client/components/segment-cache/vary-path.ts:355-383](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L355-L383) | Vary Path Type | Structure / Chain Order | Purpose | Sources | | :--- | :--- | :--- | :--- | | **`RouteVaryPath`** | `requestKey` $\rightarrow$ `searchParams` (`?`) $\rightarrow$ `nextUrl` | Identifies and caches top-level route lookups, considering URL search and Next-URL headers. | [packages/next/src/client/components/segment-cache/vary-path.ts:56-71](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L56-L71) | | **`LayoutVaryPath`** | `requestKey` $\rightarrow$ `pathParams` (chained via `PartialSegmentVaryPath`) | Caches layout segments across nested dynamic path parameters. | [packages/next/src/client/components/segment-cache/vary-path.ts:74-81](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L74-L81) | | **`PageVaryPath`** | `requestKey` $\rightarrow$ `searchParams` (`?`) $\rightarrow$ `pathParams` | Caches page segments (and metadata) incorporating search parameters alongside path parameters. | [packages/next/src/client/components/segment-cache/vary-path.ts:83-95](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L83-L95) | Sources: [packages/next/src/client/components/segment-cache/vary-path.ts:56-97](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L56-L97) > [!NOTE] > The metadata "segment" is not a physical segment within the route tree, but it behaves like a page segment during caching. Because page request keys lack path information, metadata vary paths append `HEAD_REQUEST_KEY` to a simulated request key derived from the first parallel page segment to ensure proper separation in the client cache. Sources: [packages/next/src/client/components/segment-cache/vary-path.ts:210-253](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L210-L253) Search parameters are exclusive to page segments and metadata. When determining how to access or store segment data for a request, `getSegmentVaryPathForRequest()` inspects the active `FetchStrategy` and the route tree configuration. Sources: [packages/next/src/client/components/segment-cache/vary-path.ts:255-325](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L255-L325) - **`FetchStrategy.RuntimeShell`**: Returns `tree.shellVaryPath`, substituting all non-root parameters and search parameters with `Fallback` while preserving root parameters and structural keys. Sources: [packages/next/src/client/components/segment-cache/vary-path.ts:282-288](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L282-L288), [packages/next/src/client/components/segment-cache/vary-path.ts:385-407](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L385-L407) - **Static Prefetches**: Static strategies never vary on search parameters. If `fetchStrategy` excludes search params (i.e., neither `FetchStrategy.Full` nor `FetchStrategy.PPRRuntime`), the search parameter node in the page vary path is patched with `Fallback`. Sources: [packages/next/src/client/components/segment-cache/vary-path.ts:293-320](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L293-L320) - **Runtime Full / PPRRuntime Prefetches**: Preserves the concrete search parameter value (`renderedSearch`) within the vary path node. Sources: [packages/next/src/client/components/segment-cache/vary-path.ts:297-300](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L297-L300), [packages/next/src/client/components/segment-cache/vary-path.ts:323-324](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L323-L324) Sources: [packages/next/src/client/components/segment-cache/vary-path.ts:255-325](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L255-L325) > [!TIP] > Use `clonePageVaryPathWithNewSearchParams()` to dynamically retarget an existing `PageVaryPath` with a new normalized search string without rebuilding the entire path structure from the root tree. Sources: [packages/next/src/client/components/segment-cache/vary-path.ts:327-344](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/vary-path.ts#L327-L344) ## LRU Eviction and Memory Management The segment cache implements an in-memory Least Recently Used (LRU) doubly-linked list for tracking memory consumption across disparate value types such as route cache entries, segment cache entries, and back-forward cache entries. The cache maintains a soft memory ceiling configured by `maxLruSize`, defaulting to 50 MB. Sources: [packages/next/src/client/components/segment-cache/lru.ts:5-15](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L5-L15), [packages/next/src/client/components/segment-cache/cache-map.ts:83-86](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L83-L86) Memory tracking relies on three foundational functions exposed by the LRU module: `lruPut()`, `updateLruSize()`, and `deleteFromLru()`. When an entry is accessed or inserted, it moves to the front of the list using `lruPut()`. Sources: [packages/next/src/client/components/segment-cache/lru.ts:16-54](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L16-L54) ```typescript export function lruPut(node: UnknownMapEntry) { if (head === node) { return } const prev = node.prev const next = node.next if (next === null || prev === null) { lruSize += node.size ensureCleanupIsScheduled() } else { prev.next = next next.prev = prev } if (head === null) { node.prev = node node.next = node } else { const tail = head.prev node.prev = tail if (tail !== null) { tail.next = node } node.next = head head.prev = node } head = node } ``` Sources: [packages/next/src/client/components/segment-cache/lru.ts:16-53](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L16-L53) The call-chain execution walkthrough for updating or inserting an entry follows a precise order: 1. `setInCacheMap()` or `getFromCacheMap()` invokes `lruPut(entry)` upon accessing or inserting a node. Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:255-257](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L255-L257), [packages/next/src/client/components/segment-cache/cache-map.ts:386-388](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L386-L388) 2. `lruPut()` inspects whether `node` is already linked (`next !== null && prev !== null`). Sources: [packages/next/src/client/components/segment-cache/lru.ts:16-23](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L16-L23) 3. If unlinked (an insertion), it increments `lruSize` by `node.size` and calls `ensureCleanupIsScheduled()`. Sources: [packages/next/src/client/components/segment-cache/lru.ts:23-29](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L23-L29) 4. `ensureCleanupIsScheduled()` compares `lruSize` against `maxLruSize`; if the limit is exceeded, it triggers `pingPrefetchScheduler()`. Sources: [packages/next/src/client/components/segment-cache/lru.ts:98-107](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L98-L107) Entries can change size independently of position movements. The `updateLruSize()` function isolates resizing operations, updating `lruSize` only if the node is actively tracked by the LRU list. Sources: [packages/next/src/client/components/segment-cache/lru.ts:55-67](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L55-L67) ```typescript export function updateLruSize(node: UnknownMapEntry, newNodeSize: number) { const prevNodeSize = node.size node.size = newNodeSize if (node.next === null) { return } lruSize = lruSize - prevNodeSize + newNodeSize ensureCleanupIsScheduled() } ``` Sources: [packages/next/src/client/components/segment-cache/lru.ts:55-67](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L55-L67) > [!WARNING] > Entries exceeding the LRU size limit are not evicted immediately during mutation. Instead, cleanup is deferred to an asynchronous task by pinging the prefetch scheduler, which executes `cleanup()` once active prefetch queues and in-progress requests drain. Sources: [packages/next/src/client/components/segment-cache/lru.ts:26-29](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L26-L29), [packages/next/src/client/components/segment-cache/lru.ts:98-107](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L98-L107) When `cleanup()` runs, it continues evicting items from the tail of the LRU list until total memory usage drops to or below 90% of `maxLruSize`. Sources: [packages/next/src/client/components/segment-cache/lru.ts:109-127](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L109-L127) ```typescript export function cleanup() { if (lruSize <= maxLruSize) { return } const ninetyPercentMax = maxLruSize * 0.9 while (lruSize > ninetyPercentMax && head !== null) { const tail = head.prev if (tail !== null) { deleteMapEntry(tail) } } } ``` Sources: [packages/next/src/client/components/segment-cache/lru.ts:109-127](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L109-L127) > [!NOTE] > Read path lookups also perform lazy validation: `lazilyEvictIfNeeded()` checks whether a matched entry's value has expired via `isValueExpired()`. If expired, it calls `deleteMapEntry(entry)` immediately during the read and returns a cache miss. Sources: [packages/next/src/client/components/segment-cache/cache-map.ts:260-288](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L260-L288) | Function Name | Parameters | Purpose | Sources | | :--- | :--- | :--- | :--- | | **`lruPut`** | `node: UnknownMapEntry` | Inserts or repositions a node to the head of the LRU doubly-linked list and tracks sizing. | [packages/next/src/client/components/segment-cache/lru.ts:16-53](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L16-L53) | | **`updateLruSize`** | `node: UnknownMapEntry, newNodeSize: number` | Adjusts an entry's tracked memory footprint and schedules cleanup if capacity is exceeded. | [packages/next/src/client/components/segment-cache/lru.ts:55-67](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L55-L67) | | **`deleteFromLru`** | `deleted: UnknownMapEntry` | Unlinks a node from the LRU doubly-linked list and decrements `lruSize`. | [packages/next/src/client/components/segment-cache/lru.ts:69-96](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L69-L96) | | **`cleanup`** | None | Evicts tail entries asynchronously until LRU memory usage falls to 90% capacity. | [packages/next/src/client/components/segment-cache/lru.ts:109-127](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L109-L127) | | **`lazilyEvictIfNeeded`** | `now: number, currentCacheVersion: number, entry: MapEntry, onlyMatchFulfilled: boolean` | Evaluates expiration during read lookups, evicting stale entries on-the-fly. | [packages/next/src/client/components/segment-cache/cache-map.ts:268-298](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L268-L298) | Sources: [packages/next/src/client/components/segment-cache/lru.ts:16-127](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/lru.ts#L16-L127), [packages/next/src/client/components/segment-cache/cache-map.ts:268-298](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache-map.ts#L268-L298) ## Prefetch Task Priority and Scheduling Prefetch tasks are organized and prioritized using a min-heap scheduler backed by `taskHeap`. Tasks are processed in distinct phases to ensure that high-leverage structural work runs before per-link segment prefetching. The phases are evaluated via the `PrefetchPhase` enum: `RouteTree` fetches the route's tree structure, `Shell` fetches the reusable App Shell (param-free loading state) bounded by filesystem-route counts rather than link counts, and `Speculative` fetches concrete per-link segment data. Sources: [packages/next/src/client/components/segment-cache/scheduler.ts:168-202](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L168-L202) | Phase Name | Value / Order | Purpose | Sources | | :--- | :--- | :--- | :--- | | **`RouteTree`** | Lowest priority phase | Fetches the route's initial tree structure. | [packages/next/src/client/components/segment-cache/scheduler.ts:192-194](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L192-L194) | | **`Shell`** | Intermediate phase | Fetches the reusable App Shell (param-free loading state) for routes supporting PPR. | [packages/next/src/client/components/segment-cache/scheduler.ts:195-198](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L195-L198) | | **`Speculative`** | Highest phase number | Fetches concrete per-link segment data. | [packages/next/src/client/components/segment-cache/scheduler.ts:199-200](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L199-L200) | Sources: [packages/next/src/client/components/segment-cache/scheduler.ts:168-202](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L168-L202) New prefetch tasks are initiated via `schedulePrefetchTask()` or managed via link components through `onLinkVisibilityChanged()` and `onNavigationIntent()`. When a link enters the viewport via an `IntersectionObserver`, `onLinkVisibilityChanged()` sets `instance.isVisible = true`, adds the instance to `prefetchableAndVisible`, and reschedules its prefetch task with `PrefetchPriority.Default`. Hovering or touching a link triggers `onNavigationIntent()`, which bumps the task priority to `PrefetchPriority.Intent` and potentially upgrades the fetch strategy to `FetchStrategy.Full` if `__NEXT_DYNAMIC_ON_HOVER` is enabled. Sources: [packages/next/src/client/components/segment-cache/scheduler.ts:271-313](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L271-L313), [packages/next/src/client/components/links.ts:259-300](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts#L259-L300) > [!NOTE] > The scheduler reserves special network bandwidth for the most recently hovered or touched link (`mostRecentlyHoveredLink`), ensuring that intent-driven prefetches are not starved by background viewport tasks. Sources: [packages/next/src/client/components/segment-cache/scheduler.ts:224-228](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L224-L228) Bandwidth and request pacing are regulated by tracking active network operations (`inProgressRequests`) and enforcing revalidation cooldowns. When server action revalidations occur, `startRevalidationCooldown()` initiates a 300ms cooldown period (`REVALIDATION_COOLDOWN_MS`) during which prefetch requests are blocked to allow CDN cache propagation before retrying the prefetch queue via `pingPrefetchScheduler()`. Sources: [packages/next/src/client/components/segment-cache/scheduler.ts:219-254](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L219-L254) ## Navigation Read Path and PPR Hydration Client navigation read paths and Partial Prerendering (PPR) hydration coordinate through segment cache lookups, route tree diffing, and `CacheNode` assembly. When a navigation is initiated, the router checks existing cache entries using lookup helpers like `readSegmentCacheEntryForNavigation()`. This function performs up to two lookups: first searching for a fulfilled fallback entry at more-specific keypaths, and if none is found, falling back to a regular lookup to return the most specific match regardless of status. Sources: [packages/next/src/client/components/segment-cache/cache.ts:503-527](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L503-L527) To transition between routes, the router compares incoming segments against existing ones using `compareSegments()`, which classifies the relationship into distinct match variants. Reused shared cache nodes carry forward their `scrollRef` to preserve scroll intent across tree rebuilds and retain `bfcacheId` values so shared-layout segments keep a stable identity across navigations. Sources: [packages/next/src/client/components/router-reducer/ppr-navigations.ts:894-911](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L894-L911), [packages/next/src/client/components/router-reducer/ppr-navigations.ts:1326-1342](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L1326-L1342) | Segment Match Kind | Condition | Meaning | Sources | | :--- | :--- | :--- | :--- | | **`Match`** | `matchSegment(newSegment, oldSegment)` returns true | Two segments are equivalent; the `CacheNode` can be reused as-is. | [packages/next/src/client/components/router-reducer/ppr-navigations.ts:1330-1332](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L1330-L1332) | | **`SearchParamOnlyChange`** | Both segments are page strings starting with `PAGE_SEGMENT_KEY` but fail structural match | Page segments differ only in search params; the `CacheNode` is rebuilt while carrying forward the `bfcacheId`. | [packages/next/src/client/components/router-reducer/ppr-navigations.ts:1333-1340](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L1333-L1340) | | **`Change`** | Default fallback case | Segments differ in routing structure; the `CacheNode` must be created fresh. | [packages/next/src/client/components/router-reducer/ppr-navigations.ts:1341-1341](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L1341-L1341) | Sources: [packages/next/src/client/components/router-reducer/ppr-navigations.ts:1312-1342](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L1312-L1342) > [!WARNING] > Two successive route tree mismatches trigger a fallback to an MPA navigation to prevent infinite retry loops when server redirects or rewrites invalidate optimistic route predictions. Sources: [packages/next/src/client/components/router-reducer/ppr-navigations.ts:1344-1347](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L1344-L1347) Cache nodes are assembled via `createCacheNode()`, combining server-rendered React nodes, prefetch React payloads, head data, prefetch head data, and back-forward cache identifiers. During rendering, `InnerLayoutRouter` and associated boundary handlers iterate over router back-forward cache entries (`RouterBFCacheEntry`), wrapping each node in `Activity` boundaries with visibility modes determined by state key equality against the active state key. Sources: [packages/next/src/client/components/router-reducer/ppr-navigations.ts:1279-1296](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L1279-L1296), [packages/next/src/client/components/layout-router.tsx:688-693](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/layout-router.tsx#L688-L693), [packages/next/src/client/components/layout-router.tsx:842-852](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/layout-router.tsx#L842-L852) > [!NOTE] > Server-side rendering and initial client-side hydration trees use a fixed sentinel `bfcacheId` of `0` to reconcile cleanly across hydration, whereas subsequent client-side navigations increment a globally unique counter via `generateBFCacheId()`. Sources: [packages/next/src/client/components/router-reducer/ppr-navigations.ts:1301-1310](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L1301-L1310) ## Cache Invalidation and Lifecycle Synchronization Cache invalidation and lifecycle synchronization ensure that stale prefetches and expired back-forward cache entries do not pollute client navigations after server mutations. When server actions trigger revalidations, the caching layer coordinates cache evictions, CDN propagation delays, and version increments across both route and segment caches. Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:343-368](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L343-L368), [packages/next/src/client/components/segment-cache/cache.ts:424-441](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L424-L441) When a server action executes and returns an action revalidation header indicating that data has changed, `serverActionReducer()` drives the invalidation sequence. Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:343-368](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L343-L368) The invalidation call chain proceeds as follows: `serverActionReducer()` evaluates `revalidationKind` → calls `invalidateBfCache()` to increment the back-forward cache version → evaluates whether `revalidationKind === ActionDidRevalidateStaticAndDynamic` to invoke `invalidateEntirePrefetchCache(nextUrl, state.tree)` → invokes `startRevalidationCooldown()` to delay subsequent prefetches. Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:343-368](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L343-L368), [packages/next/src/client/components/segment-cache/bfcache.ts:63-68](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L63-L68) > [!CAUTION] > If a server action triggers both static and dynamic revalidation (`ActionDidRevalidateStaticAndDynamic`), the entire prefetch cache is purged via `invalidateEntirePrefetchCache()`, discarding all active segment and route entries. Sources: [packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts:361-363](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts#L361-L363) The back-forward cache (`bfcache`) stores completed navigation payloads in a specialized `CacheMap` managed by `bfcache.ts`. Stale times are calculated relative to absolute timestamps. The helper function `computeDynamicStaleAt(now, dynamicStaleTimeSeconds)` converts server-sent dynamic stale times into absolute timestamps, falling back to the global `DYNAMIC_STALETIME_MS` constant when `UnknownDynamicStaleTime` (`-1`) is received. Sources: [packages/next/src/client/components/segment-cache/bfcache.ts:15-22](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L15-L22), [packages/next/src/client/components/segment-cache/bfcache.ts:59-60](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L59-L60) Similarly, `getStaleTimeMs(staleTimeSeconds)` enforces a strict lower bound of 30 seconds on stale times to prevent excessively short-lived configurations from disabling prefetching entirely. Sources: [packages/next/src/client/components/segment-cache/cache.ts:120-122](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L120-L122) | Stale / Invalidation Function | Default Value / Sentinel | Purpose | Sources | | :--- | :--- | :--- | :--- | | **`getStaleTimeMs()`** | `Math.max(staleTimeSeconds, 30) * 1000` | Enforces a minimum 30-second stale time floor for segment prefetches. | [packages/next/src/client/components/segment-cache/cache.ts:120-122](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L120-L122) | | **`computeDynamicStaleAt()`** | `UnknownDynamicStaleTime` (`-1`) | Converts dynamic stale time seconds into an absolute `staleAt` timestamp. | [packages/next/src/client/components/segment-cache/bfcache.ts:15-22](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L15-L22) | | **`invalidateBfCache()`** | Increments `currentBfCacheVersion` | Invalidates all existing back-forward cache entries on the window object. | [packages/next/src/client/components/segment-cache/bfcache.ts:63-68](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L63-L68) | | **`startRevalidationCooldown()`** | `REVALIDATION_COOLDOWN_MS = 300` | Blocks prefetch requests temporarily to allow CDN cache propagation. | [packages/next/src/client/components/segment-cache/scheduler.ts:230-254](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L230-L254) | Sources: [packages/next/src/client/components/segment-cache/cache.ts:120-122](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L120-L122), [packages/next/src/client/components/segment-cache/bfcache.ts:15-68](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L15-L68), [packages/next/src/client/components/segment-cache/scheduler.ts:230-254](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L230-L254) To accommodate propagation delays in CDN layers following a cache revalidation, `startRevalidationCooldown()` schedules a 300-millisecond timeout (`REVALIDATION_COOLDOWN_MS`). During this window, prefetch scheduling is suppressed. If multiple revalidations occur in rapid succession, existing timeout handles are cleared and reset via `clearTimeout()`, ensuring the cooldown period extends cleanly from the final invalidation event before calling `pingPrefetchScheduler()` to resume queued tasks. Sources: [packages/next/src/client/components/segment-cache/scheduler.ts:230-254](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L230-L254) ## Related - [[Prefetching and PPR]] - [[Router State Reducer]] --- ## Technical docs: Prefetching and PPR URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-routing/prefetching-and-ppr
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/client/components/router-reducer/ppr-navigations.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts) - [packages/next/src/client/components/segment-cache/scheduler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts) - [packages/next/src/shared/lib/router/router.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/router.ts) - [packages/next/src/client/components/segment-cache/cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts) - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/client/components/segment-cache/navigation.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts) - [packages/next/src/client/components/links.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts) - [packages/next/src/client/route-loader.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/route-loader.ts) - [packages/next/src/client/dev/debug-channel.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/debug-channel.ts) - [packages/next/src/client/components/segment-cache/navigation-testing-lock.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation-testing-lock.ts) - [packages/next/src/client/components/segment-cache/prefetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/prefetch.ts) - [packages/next/src/client/components/segment-cache/bfcache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts) - [packages/next/src/client/components/segment-cache/optimistic-routes.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts)
## Overview Partial Prerendering (PPR) and prefetching form the architectural backbone of Next.js client-side navigation, optimizing application responsiveness by separating static shells from dynamic data streams. By combining viewport observation with priority heap task scheduling, Next.js proactively prefetches route trees and individual segment bundles before a user navigates, eliminating round-trip latency. Sources: [packages/next/src/client/components/segment-cache/scheduler.ts:621-627](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L621-L627), [packages/next/src/client/components/links.ts:249-300](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts#L249-L300) The Segment Cache orchestrates the storage, LRU retention, dynamic staleness computation, and mutation-driven invalidation of cached route elements. Optimistic routing exploits pattern discovery to match client route trees instantly, falling back to server resolution or rewrite handling when mismatches occur. Sources: [packages/next/src/client/components/segment-cache/cache.ts:3081-3110](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L3081-L3110), [packages/next/src/client/components/segment-cache/optimistic-routes.ts:1-44](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L1-L44) During navigation, PPR executes immediate shell rendering alongside deferred dynamic RSC fetches, reconciling router trees via copy-on-write task updates and staged payloads. Integration with the browser's back-forward cache preserves session history and dynamic segment states, while synchronization locks and development debug channels secure navigation execution and diagnostic telemetry. Sources: [packages/next/src/client/components/router-reducer/ppr-navigations.ts:163-190](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L163-L190), [packages/next/src/client/components/segment-cache/bfcache.ts:32-57](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L32-L57), [packages/next/src/client/dev/debug-channel.ts:307-359](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/debug-channel.ts#L307-L359) ## Link Prefetching and Task Scheduling ### Overview Link prefetching and task scheduling manage when and how navigation targets are identified, observed for visibility, and prioritized for prefetching. By using a shared `IntersectionObserver` across all `` components with a root margin of `200px`, Next.js observes when anchor tags enter or approach the viewport. When visibility status updates or a navigation intent is triggered via user interaction, link instances coordinate with the segment cache scheduler to initiate or reschedule background prefetch tasks. Sources: [packages/next/src/client/components/links.ts:107-141](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts#L107-L141), [packages/next/src/client/components/links.ts:249-300](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts#L249-L300) ### Viewport Observation and Trigger Mechanisms Link instances are registered using `mountLinkInstance` or `mountFormInstance`, storing references inside a prefetchable collection tracked by an `IntersectionObserver`. The observation flow proceeds through specific function calls: 1. `handleIntersect()` receives entries from the observer and determines visibility via `entry.intersectionRatio > 0`. 2. `onLinkVisibilityChanged()` updates `instance.isVisible`, adds or removes the instance from `prefetchableAndVisible`, and calls `rescheduleLinkPrefetch()` with `PrefetchPriority.Default`. 3. `onNavigationIntent()` is invoked on hover or touch events, optionally upgrading the fetch strategy to `FetchStrategy.Full` when `__NEXT_DYNAMIC_ON_HOVER` and `unstable_upgradeToDynamicPrefetch` are enabled, and reschedules the prefetch with `PrefetchPriority.Intent`. Sources: [packages/next/src/client/components/links.ts:121-141](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts#L121-L141), [packages/next/src/client/components/links.ts:168-194](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts#L168-L194), [packages/next/src/client/components/links.ts:249-300](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts#L249-L300) > [!NOTE] > Prefetching on viewport intersection is explicitly disabled in development environments (`NODE_ENV !== 'production'`) for performance reasons to avoid compiling target pages prematurely during local inspection. > Sources: [packages/next/src/client/components/links.ts:260-265](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts#L260-L265) When visible links must be refreshed due to changes in `nextUrl`, the root route tree, or cache invalidations, `pingVisibleLinks()` iterates over `prefetchableAndVisible`. If `isPrefetchTaskDirty()` returns true, it cancels the existing task via `cancelPrefetchTask()` and schedules a new one. Sources: [packages/next/src/client/components/links.ts:354-386](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts#L354-L386) ### Programmatic Prefetching API Beyond automatic link observation, the public `prefetch` function serves as the direct entrypoint for imperative prefetching through router methods or custom link wrappers. It validates the target URL via `createPrefetchURL()`, constructs a cache key incorporating any interception route `nextUrl`, and delegates task creation to the scheduler. Sources: [packages/next/src/client/components/segment-cache/prefetch.ts:27-47](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/prefetch.ts#L27-L47) | Function / Method | Input Parameters | Return Type | Purpose | | :--- | :--- | :--- | :--- | | `mountLinkInstance` | `element, href, router, fetchStrategy, prefetchEnabled, setOptimisticLinkStatus, ownerStack` | `LinkInstance` | Registers a link element, coerces its URL, and initiates viewport visibility observation if enabled. | | `mountFormInstance` | `element, href, router, fetchStrategy` | `void` | Registers a form element for prefetch observation based on its action URL. | | `onLinkVisibilityChanged` | `element, isVisible` | `void` | Updates link visibility state, manages visible tracking sets, and triggers default priority rescheduling. | | `onNavigationIntent` | `element, unstable_upgradeToDynamicPrefetch` | `void` | Handles hover or touch interactions, optionally upgrading fetch strategy and rescheduling with intent priority. | | `pingVisibleLinks` | `nextUrl, tree` | `void` | Iterates over visible links, checks for dirty cache states, cancels stale tasks, and reschedules work. | | `prefetch` | `href, nextUrl, treeAtTimeOfPrefetch, fetchStrategy, onInvalidate` | `void` | Validates an imperative prefetch URL and schedules a prefetch task with default priority and invalidation callback. | Sources: [packages/next/src/client/components/links.ts:168-232](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts#L168-L232), [packages/next/src/client/components/links.ts:259-300](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts#L259-L300), [packages/next/src/client/components/links.ts:354-386](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/links.ts#L354-L386), [packages/next/src/client/components/segment-cache/prefetch.ts:27-47](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/prefetch.ts#L27-L47) ## Segment Cache Storage and Invalidation ### Overview The segment cache manages the storage, lifecycle states, and retention of prefetched React Server Component (RSC) route trees and individual route segments. Stored entries transition through defined status stages while tracking dynamic staleness and vary-path parameters to prevent data races during parallel prefetches. Sources: [packages/next/src/client/components/segment-cache/scheduler.ts:710-726](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L710-L726), [packages/next/src/client/components/segment-cache/cache.ts:2747-2863](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L2747-L2863) ### Segment Entry Lifecycle and Runtime Fulfills When a prefetch or runtime request writes server responses into the cache, entries move from uninitialized states to fulfilled or rejected cache records. The function call chain governing runtime entry fulfillment and storage proceeds through: `writeSeedDataIntoCache()` → `fulfillEntrySpawnedByRuntimePrefetch()` → `fulfillSegmentCacheEntry()` → `setInCacheMap()` or `upsertSegmentEntry()` Sources: [packages/next/src/client/components/segment-cache/cache.ts:2686-2863](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L2686-L2863) During this flow, `writeSeedDataIntoCache` recursively unpacks seed data slots. `fulfillEntrySpawnedByRuntimePrefetch` determines whether to re-key the entry under a more generic vary path using `getFulfilledSegmentVaryPath` or `tree.shellVaryPath`. It checks if an entry is owned by the current task via `entriesOwnedByCurrentTask.get(tree.requestKey)`. If owned, it fulfills the existing entry; otherwise, it creates a detached entry or upserts it into the global `segmentCacheMap`. Sources: [packages/next/src/client/components/segment-cache/cache.ts:2686-2863](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L2686-L2863) > [!WARNING] > Never write directly over an entry created by a different task without checking task ownership; doing so introduces data races across concurrent prefetch streams. > Sources: [packages/next/src/client/components/segment-cache/cache.ts:2796-2802](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L2796-L2802) ### Dynamic Staleness and Stale-Time Resolution Entries maintain expiration timestamps (`staleAt`) calculated from server-sent headers or async iterables. `getStaleAt` evaluates an optional `staleTimeIterable` by iterating through yielded values and taking the final timestamp, falling back to `getStaleAtFromHeader` or a default static staleness window (`STATIC_STALETIME_MS`). For route tree misses where requests take longer than a minute, a temporary `staleAt` of `now + 60 * 1000` is assigned so that subsequent requests retry instead of blocking indefinitely. Sources: [packages/next/src/client/components/segment-cache/scheduler.ts:702-709](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L702-L709), [packages/next/src/client/components/segment-cache/cache.ts:3070-3109](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L3070-L3109) | Entry Status / Parameter | Source Value / Constant | Meaning / Handling | | :--- | :--- | :--- | | `EntryStatus.Empty` | Uninitialized / Cache miss | No request in progress; spawns a fetch task and upgrades state. | | `EntryStatus.Pending` | In-progress request | A fetch is underway; subsequent tasks attach to `blockedTasks`. | | `EntryStatus.Fulfilled` | Complete cache record | Contains valid RSC data, `staleAt` timestamp, and `isPartial` flag. | | `EntryStatus.Rejected` | Failed load / 404 | Request failed or was rejected inside a loading boundary. | | `STATIC_STALETIME_MS` | Fallback duration | Default staleness window applied when no server header or iterable is present. | Sources: [packages/next/src/client/components/segment-cache/scheduler.ts:684-731](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/scheduler.ts#L684-L731), [packages/next/src/client/components/segment-cache/cache.ts:3060-3109](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L3060-L3109) ## Optimistic Routing and Route Matching ### Overview Optimistic Routing enables the client to predict route structures for URLs that have not yet been prefetched by leveraging previously learned route patterns. Stored in a trie indexed by URL path segments (`KnownRoutePart`), these patterns map URL structures to route templates. When a user navigates to a URL with no direct prefetch cache entry, the client matches the candidate URL against the known route tree to synthesize a route entry instantly, avoiding a prefetch round-trip. Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:1-44](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L1-L44) ### Route Pattern Discovery and Trie Population When the server returns a route tree during an initial load, navigation, or prefetch, the client calls `discoverKnownRoute()`. This function parses the pathname into segments and invokes `discoverKnownRoutePart()`, which walks the route tree and URL parts in parallel to populate the trie. Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:199-272](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L199-L272), [packages/next/src/client/components/segment-cache/optimistic-routes.ts:345-362](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L345-L362) The call-chain execution walkthrough for discovering and caching a known route proceeds through: `discoverKnownRoute()` → `fulfillRouteCacheEntry()` → `discoverKnownRoutePart()` → `writeRouteIntoCache()` or `readPattern()` Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:199-272](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L199-L272) During this recursion, `discoverKnownRoutePart` evaluates whether a segment is static or dynamic, records static siblings into `staticChildren`, and caches the resulting route template in `knownRoutePart.pattern`. Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:370-599](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L370-L599) > [!WARNING] > If a static segment or dynamic boundary in the URL does not match the route tree structure, discovery immediately aborts trie population via `handleMismatchDueToRewrite()`, preventing malformed pattern predictions while still writing the valid entry into the standard cache. > Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:275-306](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L275-L306), [packages/next/src/client/components/segment-cache/optimistic-routes.ts:376-388](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L376-L388) ### Client Route Tree Matching and Reification When looking up an uncached route, `matchKnownRoute()` splits the pathname and invokes `matchKnownRoutePart()`. Matching prioritizes static child nodes before evaluating dynamic children (`[param]`, `[...param]`, `[[...param]]`), collecting parameter values in a `ResolvedParams` map. Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:607-621](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L607-L621), [packages/next/src/client/components/segment-cache/optimistic-routes.ts:716-777](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L716-L777) Once a matching pattern is found, `reifyRouteTree()` clones the template route tree, substituting the resolved parameter values into dynamic segments and recomputing vary paths to generate a concrete synthetic entry (`FulfilledRouteCacheEntry`). Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:648-693](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L648-L693), [packages/next/src/client/components/segment-cache/optimistic-routes.ts:879-982](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L879-L982) | Dynamic Parameter Type | Source Identifier | Runtime Matching Behavior | | :--- | :--- | :--- | | Regular Dynamic | `'d'` | Consumes exactly 1 URL part and recurses deeper to find leaf patterns. | | Required Catch-All | `'c'` | Consumes 1 or more remaining URL parts (`pathnameParts.slice(partIndex)`). | | Optional Catch-All | `'oc'` | Consumes 0 or more URL parts; defaults to an empty array when `urlPart` is null. | | Intercepted Routes | `'ci(...)'`, `'di(...)'` | Bails out to server resolution because behavior depends on navigation referrer context. | Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:780-845](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L780-L845) > [!NOTE] > The trie distinguishes between a `null` and an empty `Map` for `staticChildren`: `null` indicates that static siblings are completely unknown (such as in webpack development mode on-demand compilation), forcing the matcher to deopt to server resolution rather than risk false-positive dynamic matches. > Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:98-106](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L98-L106), [packages/next/src/client/components/segment-cache/optimistic-routes.ts:732-741](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L732-L741) ### Rewrite Fallback Handling Optimistic routing incorporates protection against dynamic rewrites and path mismatches. If the server returns a response whose pathname diverges from what was predicted, the route entry is marked with `hasDynamicRewrite = true`. Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:227-230](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L227-L230), [packages/next/src/client/components/segment-cache/optimistic-routes.ts:564-567](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L564-L567) When `matchKnownRoute` encounters a pattern where `hasDynamicRewrite` is true, or where `couldBeIntercepted` is set, it rejects the prediction and returns `null`, forcing the router to fall back to standard server resolution. Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:641-643](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L641-L643), [packages/next/src/client/components/segment-cache/optimistic-routes.ts:735-738](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L735-L738) | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | Trie-based `KnownRoutePart` storage | Fast O(path length) client-side lookups | Append-only structure with no eviction inside sessions | | Pattern reification via cloning | Instantly produces valid synthetic cache entries | Allocates new tree structures on cache prediction hits | | Static children priority over dynamic | Prevents static routes from capturing dynamic matches | Requires tracking static siblings during discovery | Sources: [packages/next/src/client/components/segment-cache/optimistic-routes.ts:33-44](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L33-L44), [packages/next/src/client/components/segment-cache/optimistic-routes.ts:745-748](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L745-L748), [packages/next/src/client/components/segment-cache/optimistic-routes.ts:879-885](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/optimistic-routes.ts#L879-L885) ## PPR Navigation Execution and Reconciliation ### Overview Partial Prerendering (PPR) navigation execution bridges client-side cache traversal and server-driven dynamic updates. When a user navigates to a new location via `navigate()`, the router checks the route segment cache for a fulfilled entry. If a matching route tree is found, it immediately builds a copy-on-write `NavigationTask` and patches the app router state, allowing the static prefetch shell to render instantly while any missing dynamic data is deferred. Sources: [packages/next/src/client/components/segment-cache/navigation.ts:58-148](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L58-L148), [packages/next/src/client/components/router-reducer/ppr-navigations.ts:163-190](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L163-L190) ### Navigation Execution Walkthrough The navigation and reconciliation pipeline flows through a series of deterministic functions that transition raw URL requests into updated router trees and dynamic server fetches: `navigate()` → `navigateImpl()` → `navigateUsingPrefetchedRouteTree()` → `navigateToKnownRoute()` → `startPPRNavigation()` → `updateCacheNodeOnNavigation()` Sources: [packages/next/src/client/components/segment-cache/navigation.ts:58-387](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L58-L387), [packages/next/src/client/components/router-reducer/ppr-navigations.ts:190-228](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L190-L228) 1. `navigate()` acts as the entry point, coordinating testing locks and calling `navigateImpl()`. Sources: [packages/next/src/client/components/segment-cache/navigation.ts:58-112](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L58-L112) 2. `navigateImpl()` queries the route cache using `readRouteCacheEntry()`. If fulfilled, it invokes `navigateUsingPrefetchedRouteTree()`. Sources: [packages/next/src/client/components/segment-cache/navigation.ts:114-149](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L114-L149) 3. `navigateUsingPrefetchedRouteTree()` extracts the target `RouteTree` and delegates to `navigateToKnownRoute()`. Sources: [packages/next/src/client/components/segment-cache/navigation.ts:360-387](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L360-L387) 4. `navigateToKnownRoute()` sets up a `NavigationRequestAccumulation` context and invokes `startPPRNavigation()`. Sources: [packages/next/src/client/components/segment-cache/navigation.ts:214-328](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L214-L328) 5. `startPPRNavigation()` wraps the root refresh state and calls `updateCacheNodeOnNavigation()`. Sources: [packages/next/src/client/components/router-reducer/ppr-navigations.ts:190-228](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L190-L228) 6. `updateCacheNodeOnNavigation()` compares the new route segments against the old `FlightRouterState`, determining whether to reuse cached nodes or switch to `createCacheNodeOnNavigation()` for divergent subtrees. Sources: [packages/next/src/client/components/router-reducer/ppr-navigations.ts:230-256](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L230-L256) > [!NOTE] > If `startPPRNavigation()` returns `null`, indicating that no SPA-compatible transitions could be resolved, the router falls back to `completeHardNavigation()`, executing a traditional full-page MPA reload. > Sources: [packages/next/src/client/components/segment-cache/navigation.ts:356-358](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L356-L358) ### Freshness Policies and Task Status Navigation tasks and freshness rules dictate how cache entries and server requests interact during routing operations. | Enum / Type Name | Member / Value | Purpose & Runtime Behavior | | :--- | :--- | :--- | | `FreshnessPolicy` | `Default` | Standard user-initiated navigation through links or router pushes. | | `FreshnessPolicy` | `Hydration` | Initial application load from server-embedded HTML seed data. | | `FreshnessPolicy` | `HistoryTraversal` | Browser back/forward history navigation restoring cached states. | | `FreshnessPolicy` | `RefreshAll` | Full router refresh explicitly requested by user actions or triggers. | | `FreshnessPolicy` | `HMRRefresh` | Development-mode Fast Refresh updating modified modules. | | `FreshnessPolicy` | `Gesture` | Pointer gesture or hover-triggered prefetch hint navigation. | | `NavigationTaskStatus` | `Pending` | Task requires a dynamic server request to resolve missing holes. | | `NavigationTaskStatus` | `Fulfilled` | Task is fully static or populated with available cache seed data. | | `NavigationTaskStatus` | `Rejected` | Task failed or encountered a route tree mismatch. | | `NavigationTaskExitStatus` | `Done` | No additional navigation actions or retries are required. | | `NavigationTaskExitStatus` | `SoftRetry` | Data failed to load due to tree mismatch; retry soft navigation. | | `NavigationTaskExitStatus` | `HardRetry` | Unrecoverable failure in parallel route; fall back to MPA retry. | Sources: [packages/next/src/client/components/router-reducer/ppr-navigations.ts:66-118](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts#L66-L118) > [!WARNING] > During gesture navigations (`FreshnessPolicy.Gesture`), dynamic request spawning is deliberately suppressed by `navigateToKnownRoute()` to avoid wasteful server invocations on mere hover events before an actual click occurs. > Sources: [packages/next/src/client/components/segment-cache/navigation.ts:330-341](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation.ts#L330-L341) ## Server Prerendering and Staged Payloads ### Overview During static generation with Partial Prerendering (PPR) enabled (`experimental.isRoutePPREnabled`), Next.js manages server prerendering through a staged process that isolates dynamic holes, tracks dynamic access patterns, and produces static Flight streams. Sources: [packages/next/src/server/app-render/app-render.tsx:8052-8056](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L8052-L8056) ### Call-Chain Execution Walkthrough The server prerendering sequence coordinates dynamic tracking stores, RSC payload generation, React Server streaming, and HTML prelude processing: 1. `createDynamicTrackingState()` initializes dynamic tracking stores based on debug options. Sources: [packages/next/src/server/app-render/app-render.tsx:8054-8054](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L8054-L8054) 2. `workUnitAsyncStorage.run()` binds the `pprReactServerPrerenderStore` context to execute `getRSCPayload()`. Sources: [packages/next/src/server/app-render/app-render.tsx:8070-8076](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L8070-L8076) 3. `createReactServerPrerenderResultFromRender()` wraps the result of `renderFlightStream()`, producing the unclosing server stream. Sources: [packages/next/src/server/app-render/app-render.tsx:8079-8091](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L8079-L8091) 4. `getClientPrerender()` renders the `` tree using the PPR prerender store, returning an `unprocessedPrelude` and `postponed` state. Sources: [packages/next/src/server/app-render/app-render.tsx:8107-8127](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L8107-L8127) 5. `streamToBuffer()` reads the full React Server render stream into `flightData`, which is then passed to `collectSegmentData()` if `shouldGenerateStaticFlightData()` evaluates to true. Sources: [packages/next/src/server/app-render/app-render.tsx:8139-8151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L8139-L8151) 6. `processPreludeOp()` processes the unprocessed prelude to yield the final `prelude` and `preludeIsEmpty` status. Sources: [packages/next/src/server/app-render/app-render.tsx:8153-8154](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L8153-L8154) > [!NOTE] > Awaiting the complete RSC render stream via `streamToBuffer(reactServerResult.asStream())` guarantees that dynamic API usages anywhere within the Server Component tree are captured—even if those specific branches are omitted from the initial SSR HTML prelude. > Sources: [packages/next/src/server/app-render/app-render.tsx:8136-8140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L8136-L8140) ### Prerender Outcomes and Postponed State Generation When prerendering completes, Next.js inspects the dynamic access tracking to categorize the output into one of three distinct outcomes: Dynamic HTML, Dynamic Data, or fully Static. Sources: [packages/next/src/server/app-render/app-render.tsx:8156-8171](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L8156-L8171) | Prerender Outcome | Detection Condition | Handling & Postponed State | | :--- | :--- | :--- | | `Dynamic HTML` | `accessedDynamicData()` is true AND `postponed != null` | Generates postponed state via `getDynamicHTMLPostponedState()` using `DynamicHTMLPreludeState.Empty` or `Full`. | | `Dynamic Data` | `accessedDynamicData()` is true AND `postponed == null` | Generates postponed state via `getDynamicDataPostponedState()` without resuming HTML shells. | | `Static` | `accessedDynamicData()` is false | Statically encodes all server-inserted HTML and Flight data without dynamic holes. | Sources: [packages/next/src/server/app-render/app-render.tsx:8171-8189](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L8171-L8189) > [!WARNING] > If a prerender has dynamic holes (`Dynamic HTML`), the engine skips embedding server-inserted HTML and inlined Flight data into the static output, requiring runtime resumption when client requests arrive. > Sources: [packages/next/src/server/app-render/app-render.tsx:8159-8163](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L8159-L8163) ## Back Forward Cache Integration ### Overview The back-forward cache (`bfcache`) integrates with client-side routing to persist session history state, coordinate dynamic segment upgrades, and manage the cache restore lifecycle across history traversals and regular navigations. It maintains a separate memory store (`bfcacheMap`) using the `CacheMap` data structure, tracking `BFCacheEntry` records containing rendered server components (`rsc`), prefetched server components (`prefetchRsc`), page metadata (`head`), prefetched metadata (`prefetchHead`), persistent `bfcacheId` values, and staleness parameters. Sources: [packages/next/src/client/components/segment-cache/bfcache.ts:32-60](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L32-L60) ### Cache Restore Lifecycle and Operations The back-forward cache exposes several exported functions to write, read, and invalidate persisted entry states depending on navigation type and staleness conditions: - `invalidateBfCache()` increments `currentBfCacheVersion`, invalidating existing back-forward cache entries when called in a browser environment. Sources: [packages/next/src/client/components/segment-cache/bfcache.ts:63-68](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L63-L68) - `writeToBFCache()` constructs a `BFCacheEntry` with `status: EntryStatus.Fulfilled` and stores it under the provided `varyPath`. Sources: [packages/next/src/client/components/segment-cache/bfcache.ts:70-114](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L70-L114) - `writeHeadToBFCache()` delegates head-data writing directly to `writeToBFCache()`. Sources: [packages/next/src/client/components/segment-cache/bfcache.ts:116-135](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L116-L135) - `updateBFCacheEntryStaleAt()` retrieves an entry bypassing staleness checks using `-1` and updates its `staleAt` property with a per-page value from `unstable_dynamicStaleTime`. Sources: [packages/next/src/client/components/segment-cache/bfcache.ts:137-163](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L137-L163) - `readFromBFCache()` queries `bfcacheMap` passing `-1` as the timestamp to bypass staleness evaluation during back-forward history traversals. Sources: [packages/next/src/client/components/segment-cache/bfcache.ts:165-183](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L165-L183) - `readFromBFCacheDuringRegularNavigation()` evaluates entries against the real `now` timestamp during standard navigations. Sources: [packages/next/src/client/components/segment-cache/bfcache.ts:185-201](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L185-L201) > [!NOTE] > During a back-forward navigation, `readFromBFCache` passes `-1` instead of the current timestamp to `getFromCacheMap`, explicitly bypassing staleness checks so cached session history state is always restored regardless of age. > Sources: [packages/next/src/client/components/segment-cache/bfcache.ts:171-183](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L171-L183) ### Dynamic Segment Upgrades and Staleness Computation Dynamic stale times received via the Flight response `d` field are normalized into absolute timestamps via `computeDynamicStaleAt()`, falling back to `DYNAMIC_STALETIME_MS` when `UnknownDigitalStaleTime` (`-1`) is supplied. Sources: [packages/next/src/client/components/segment-cache/bfcache.ts:5-22](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L5-L22) | Function Name | Parameters | Return Type | Purpose | | :--- | :--- | :--- | :--- | | `computeDynamicStaleAt` | `now: number`, `dynamicStaleTimeSeconds: number` | `number` | Converts server-sent dynamic stale seconds into an absolute millisecond timestamp. | | `invalidateBfCache` | *none* | `void` | Increments `currentBfCacheVersion` to invalidate stale session records. | | `writeToBFCache` | `now`, `varyPath`, `rsc`, `prefetchRsc`, `head`, `prefetchHead`, `dynamicStaleAt`, `bfcacheId` | `void` | Stores a completed navigation entry in `bfcacheMap`. | | `readFromBFCache` | `varyPath: SegmentVaryPath` | `BFCacheEntry \| null` | Retrieves a cached history entry bypassing timestamp checks. | | `readFromBFCacheDuringRegularNavigation` | `now: number`, `varyPath: SegmentVaryPath` | `BFCacheEntry \| null` | Queries cached entries subject to normal TTL and staleness validation. | Sources: [packages/next/src/client/components/segment-cache/bfcache.ts:15-201](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts#L15-L201) ## Navigation Locks and Debugging Channels ### Instant Navigation Testing Synchronization Locks The Instant Navigation Testing API manages synchronization via an in-memory lock (`NavigationLockState`) and a persistent cookie (`NEXT_INSTANT_TEST_COOKIE`). When an external testing harness or devtools initiates a capture scope, it creates a pending cookie state. Next.js reads this state, acquires the lock, and intercepts outgoing client fetches. | Cookie State | Raw Value Condition | Meaning | | :--- | :--- | :--- | | `empty` | `raw === ''` | No testing lock cookie is present. | | `pending` | `parsed[2]` is neither `null` nor present as object | External actor initiated a pending navigation test scope. | | `mpa` | `parsed[2] === null` | Static shell served; captured Multi-Page Application mode. | | `spa` | `parsed[2]` is an object (`from` / `to` tree) | Prefetch resolved; captured Single-Page Application mode. | Sources: [packages/next/src/client/components/segment-cache/navigation-testing-lock.ts:21-37](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation-testing-lock.ts#L21-L37) The lock lifecycle transitions through exact internal functions: `startListeningForInstantNavigationCookie()` inspects initial state and attaches listeners, calling `acquireLock()` to instantiate a promise and override `window.fetch` with `globalFetchOverride`, and invoking `releaseLock()` alongside `refreshOnInstantNavigationUnlock()` when the test cookie is deleted. Sources: [packages/next/src/client/components/segment-cache/navigation-testing-lock.ts:87-118](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation-testing-lock.ts#L87-L118), [packages/next/src/client/components/segment-cache/navigation-testing-lock.ts:158-216](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation-testing-lock.ts#L158-L216) > [!WARNING] > `globalFetchOverride` pins execution to the pre-lock `window.fetch` captured during `acquireLock()`. If a user-installed fetch override is attached after the lock scope begins, it remains bypassed until the navigation lock is fully released and the original fetch reference is restored. > Sources: [packages/next/src/client/components/segment-cache/navigation-testing-lock.ts:131-150](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/navigation-testing-lock.ts#L131-L150) ### Development Debug Communication Streams The development debug channel streams diagnostic chunks for requests identified by `NEXT_REQUEST_ID_HEADER` or `self.__next_r`. Initial document debug streams are buffered using a `TransformStream`, written asynchronously to IndexedDB (`__next_debug_channel` database under the `channels` store) during idle periods (`whenIdle()`), and pruned to maintain a maximum bound of `10` entries using the `createdAt` index. | Constant Name | Value | Purpose | | :--- | :--- | :--- | | `DB_NAME` | `'__next_debug_channel'` | IndexedDB database identifier for persisted debug chunks. | | `STORE_NAME` | `'channels'` | Object store holding `DebugChannelEntry` records keyed by `requestId`. | | `CREATED_AT_INDEX` | `'createdAt'` | Index name used for ordered cursor traversal and pruning. | | `MAX_ENTRIES` | `10` | Maximum number of debug entries retained before oldest pruning occurs. | Sources: [packages/next/src/client/dev/debug-channel.ts:11-20](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/debug-channel.ts#L11-L20) When a cached HTML document is restored from the browser cache, `createDebugChannel()` evaluates navigation timing metrics via `wasServedFromCacheKnownAtExec()` and `wasServedFromCacheAtPageshow()`. If chunks are missing from IndexedDB during a cache restore, `restoreDebugChannelOrReload()` triggers an unconditional `location.reload()` while parking the stream to prevent hydration errors. Sources: [packages/next/src/client/dev/debug-channel.ts:200-285](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/debug-channel.ts#L200-L285), [packages/next/src/client/dev/debug-channel.ts:361-432](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/debug-channel.ts#L361-L432) > [!NOTE] > `wasServedFromCacheKnownAtExec()` checks Safari's tab-duplication signature (`type === 'navigate'`, `responseStart === 0`, `responseEnd > 0`) alongside standard `transferSize` and `encodedBodySize` metrics to distinguish HTTP cache restorations from fresh server fetches prior to the `pageshow` event. > Sources: [packages/next/src/client/dev/debug-channel.ts:200-251](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/debug-channel.ts#L200-L251) ## Related - [[Client Segment Cache]] - [[Staged Dynamic Rendering]] --- ## Technical docs: Navigation Boundaries URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-routing/navigation-boundaries
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/client/components/http-access-fallback/error-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/error-boundary.tsx) - [packages/next/src/client/components/dev-root-http-access-fallback-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/dev-root-http-access-fallback-boundary.tsx) - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/client/components/redirect-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/redirect-boundary.tsx) - [packages/next/src/client/components/http-access-fallback/http-access-fallback.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/http-access-fallback.ts) - [packages/next/src/client/components/error-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/error-boundary.tsx) - [packages/next/src/next-devtools/dev-overlay/container/errors.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/container/errors.tsx) - [packages/next/src/client/components/layout-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/layout-router.tsx) - [packages/next/src/client/components/unauthorized.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/unauthorized.ts) - [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx) - [packages/next/src/client/components/catch-error.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/catch-error.tsx) - [packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx) - [packages/next/src/client/components/app-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/app-router.tsx) - [packages/next/src/client/react-client-callbacks/error-boundary-callbacks.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/react-client-callbacks/error-boundary-callbacks.ts) - [packages/next/src/client/components/builtin/unauthorized.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/builtin/unauthorized.tsx) - [packages/next/src/client/components/forbidden.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/forbidden.ts) - [packages/next/src/client/components/navigation.react-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.react-server.ts) - [packages/next/src/client/components/errors/graceful-degrade-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/errors/graceful-degrade-boundary.tsx) - [packages/next/src/client/components/is-next-router-error.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/is-next-router-error.ts) - [packages/next/src/next-devtools/dev-overlay/components/overview/segment-boundary-trigger.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/overview/segment-boundary-trigger.tsx) - [packages/next/src/lib/framework/boundary-components.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/framework/boundary-components.tsx) - [packages/next/src/next-devtools/userspace/pages/pages-dev-overlay-error-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/pages/pages-dev-overlay-error-boundary.tsx) - [packages/next/src/client/components/not-found.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/not-found.ts) - [packages/next/src/client/components/navigation.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/navigation.ts) - [packages/next/src/client/components/nav-failure-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/nav-failure-handler.ts) - [packages/next/src/next-devtools/userspace/app/client-entry.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/client-entry.tsx) - [packages/next/src/next-devtools/dev-overlay/container/runtime-error/render-error.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/container/runtime-error/render-error.tsx) - [packages/next/src/api/error.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/api/error.ts) - [packages/next/src/client/components/builtin/forbidden.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/builtin/forbidden.tsx) - [packages/next/src/client/components/builtin/global-not-found.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/builtin/global-not-found.tsx)
## Overview Navigation Boundaries form the core runtime error-interception and fallback mechanism in Next.js App Router. During client-side navigation, server rendering, or component tree execution, React rendering can be interrupted by explicit signals thrown from user code or unhandled exceptions. Instead of letting these runtime failures crash the entire React root, Next.js intercepts specific router control signals and HTTP access errors (`notFound()`, `forbidden()`, `unauthorized()`, and `redirect()`) via specialized React error boundaries placed around route segments. Sources: [packages/next/src/client/components/http-access-fallback/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/error-boundary.tsx#L1-L182) This subsystem solves the problem of granular recovery: when an error or access interruption occurs in a leaf segment, it should not dismantle parent layouts or root chrome unless explicitly required. Sources: [packages/next/src/client/components/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/error-boundary.tsx#L1-L182) The key design decisions embody throwing structured Error objects with specific `digest` signatures (such as `NEXT_HTTP_ERROR_FALLBACK;404`), inspecting these errors via `isNextRouterError`, and bubbling unhandled router signals up to parent segments while catching general JavaScript exceptions in component-level `ErrorBoundary` or `catchError` handlers. Sources: [packages/next/src/client/components/redirect-boundary.tsx:1-87](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/redirect-boundary.tsx#L1-L87) ```mermaid flowchart TD A["User Code / Server Action"] --> B{"Thrown Exception"} B -->|isRedirectError| C["RedirectErrorBoundary
(Trigger router.push / replace)"] B -->|isHTTPAccessFallbackError| D["HTTPAccessFallbackBoundary
(Render 404 / 403 / 401 fallback)"] B -->|isNextRouterError == false| E["ErrorBoundary / CatchError
(Render errorComponent & support reset/retry)"] E --> F{"Is Hard Navigation Failure?"} F -->|Yes| G["window.location.href fallback
(nav-failure-handler)"] F -->|No| H["Display error boundary UI"] ``` Sources: [packages/next/src/client/components/http-access-fallback/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/error-boundary.tsx#L1-L182) --- ## Router Error Classification and Signature Protocol Next.js categorizes thrown routing exceptions to differentiate expected navigation interruptions from unexpected runtime application bugs. Sources: [packages/next/src/client/components/is-next-router-error.ts:1-17](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/is-next-router-error.ts#L1-L17) The helper function `isNextRouterError` evaluates whether an unknown thrown value is either a redirect error or an HTTP access fallback error. Sources: [packages/next/src/client/components/is-next-router-error.ts:1-17](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/is-next-router-error.ts#L1-L17) ```typescript export function isNextRouterError( error: unknown ): error is RedirectError | HTTPAccessFallbackError { return isRedirectError(error) || isHTTPAccessFallbackError(error) } ``` Sources: [packages/next/src/client/components/is-next-router-error.ts:1-17](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/is-next-router-error.ts#L1-L17) HTTP access errors are identified via a prefixed `digest` string property on standard JavaScript `Error` instances. Sources: [packages/next/src/client/components/http-access-fallback/http-access-fallback.ts:1-62](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/http-access-fallback.ts#L1-L62) The prefix constant `HTTP_ERROR_FALLBACK_ERROR_CODE` is set to `'NEXT_HTTP_ERROR_FALLBACK'`, followed by a semicolon and the HTTP status code. Sources: [packages/next/src/client/components/http-access-fallback/http-access-fallback.ts:1-62](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/http-access-fallback.ts#L1-L62) | Function / Constant | Source Representation | Purpose | | :--- | :--- | :--- | | `HTTP_ERROR_FALLBACK_ERROR_CODE` | `'NEXT_HTTP_ERROR_FALLBACK'` | String prefix identifying HTTP access fallback errors in digests. | | `notFound()` | `DIGEST = 'NEXT_HTTP_ERROR_FALLBACK;404'` | Throws an error caught by `HTTPAccessFallbackBoundary` to render 404. | | `forbidden()` | `DIGEST = 'NEXT_HTTP_ERROR_FALLBACK;403'` | Throws an error caught by `HTTPAccessFallbackBoundary` to render 403. | | `unauthorized()` | `DIGEST = 'NEXT_HTTP_ERROR_FALLBACK;401'` | Throws an error caught by `HTTPAccessFallbackBoundary` to render 401. | Sources: [packages/next/src/client/components/http-access-fallback/http-access-fallback.ts:1-62](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/http-access-fallback.ts#L1-L62) --- ## HTTP Access Fallback Boundaries (`HTTPAccessFallbackBoundary`) The `HTTPAccessFallbackBoundary` and its underlying stateful component `HTTPAccessFallbackErrorBoundary` handle HTTP access errors such as `404 Not Found`, `403 Forbidden`, and `401 Unauthorized`. Sources: [packages/next/src/client/components/http-access-fallback/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/error-boundary.tsx#L1-L182) When `notFound()`, `forbidden()`, or `unauthorized()` is invoked in a Server Component, Route Handler, or Server Action, it interrupts rendering by throwing a digested error. Sources: [packages/next/src/client/components/http-access-fallback/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/error-boundary.tsx#L1-L182) `HTTPAccessFallbackErrorBoundary` catches this error in `getDerivedStateFromError`: Sources: [packages/next/src/client/components/http-access-fallback/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/error-boundary.tsx#L1-L182) ```typescript static getDerivedStateFromError(error: unknown) { if (isHTTPAccessFallbackError(error)) { const httpStatus = getAccessFallbackHTTPStatus(error) return { triggeredStatus: httpStatus, } } // Re-throw if error is not for 404 throw error } ``` Sources: [packages/next/src/client/components/http-access-fallback/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/error-boundary.tsx#L1-L182) During render execution, if `triggeredStatus` is set and matches an available fallback component prop (`notFound`, `forbidden`, or `unauthorized`), the boundary injects a `` tag, appends development-mode metadata tags if applicable, and renders the corresponding fallback component. Sources: [packages/next/src/client/components/http-access-fallback/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/error-boundary.tsx#L1-L182) > [!NOTE] > Navigation updates automatically reset the error boundary state. `getDerivedStateFromProps` compares `props.pathname` to `state.previousPathname`; if a navigation has occurred (`props.pathname !== state.previousPathname`), `triggeredStatus` is reset to `undefined`. Sources: [packages/next/src/client/components/http-access-fallback/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/error-boundary.tsx#L1-L182) --- ## Redirect Error Boundaries (`RedirectBoundary`) Redirect operations initiated by `redirect()` or `permanentRedirect()` throw a specialized redirect error containing target URL and redirect type metadata (`push` or `replace`). Sources: [packages/next/src/client/components/redirect-boundary.tsx:1-87](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/redirect-boundary.tsx#L1-L87) The `RedirectBoundary` component intercepts these errors to execute client-side navigation transitions without requiring full page reloads. Sources: [packages/next/src/client/components/redirect-boundary.tsx:1-87](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/redirect-boundary.tsx#L1-L87) The execution sequence is managed through `RedirectErrorBoundary` and `HandleRedirect`: Sources: [packages/next/src/client/components/redirect-boundary.tsx:1-87](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/redirect-boundary.tsx#L1-L87) ```mermaid sequenceDiagram participant UserComponent as "User Component" participant ErrorBoundary as "RedirectErrorBoundary" participant Handler as "HandleRedirect" participant Router as "AppRouterInstance" UserComponent->>ErrorBoundary: throw redirect error ErrorBoundary->>ErrorBoundary: getDerivedStateFromError() extracts URL & type ErrorBoundary->>Handler: render with URL & reset callback Handler->>Router: useEffect triggers startTransition() -> router.push / replace Handler->>ErrorBoundary: invoke reset() clearing state ``` Sources: [packages/next/src/client/components/redirect-boundary.tsx:1-87](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/redirect-boundary.tsx#L1-L87) If an error has already been marked as handled (`'handled' in error`), `RedirectErrorBoundary` catches the error solely to trigger a subtree remount without executing duplicate router navigation commands. Sources: [packages/next/src/client/components/redirect-boundary.tsx:1-87](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/redirect-boundary.tsx#L1-L87) --- ## Component-Level Error Boundaries (`ErrorBoundary` and `catchError`) General runtime errors thrown during React rendering are caught by `ErrorBoundary` (backed by `ErrorBoundaryHandler`) or the granular HOC wrapper `catchError`. Sources: [packages/next/src/client/components/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/error-boundary.tsx#L1-L182), [packages/next/src/client/components/catch-error.tsx:1-221](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/catch-error.tsx#L1-L221) Both components implement guard logic in `getDerivedStateFromError` to inspect incoming exceptions: Sources: [packages/next/src/client/components/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/error-boundary.tsx#L1-L182) ```typescript static getDerivedStateFromError( thrownValue: unknown ): Partial { if (isNextRouterError(thrownValue)) { // Re-throw if an expected internal Next.js router error occurs // this means it should be handled by a different boundary (such as a NotFound boundary in a parent segment) throw thrownValue } return { error: { thrownValue } } } ``` Sources: [packages/next/src/client/components/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/error-boundary.tsx#L1-L182) > [!IMPORTANT] > The guard `if (isNextRouterError(thrownValue)) { throw thrownValue }` is critical. It guarantees that router navigation signals (`redirect`, `notFound`, `forbidden`, `unauthorized`) are never swallowed by generic React error components, allowing them to propagate past component error boundaries straight to their respective HTTP or redirect boundary handlers. Sources: [packages/next/src/client/components/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/error-boundary.tsx#L1-L182) When a non-router runtime error is caught, `ErrorBoundaryHandler` renders `errorStyles`, `errorScripts`, and the supplied `errorComponent`, providing an `ErrorInfo` object containing `error`, `reset`, and `retry` functions. Sources: [packages/next/src/client/components/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/error-boundary.tsx#L1-L182) Calling `retry()` triggers an asynchronous React transition that calls `router.refresh()` alongside resetting local error state. Sources: [packages/next/src/client/components/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/error-boundary.tsx#L1-L182) --- ## Navigation Failure Recovery and Hard Navigation Fallbacks When an exception or rejection occurs while a navigation operation is pending, Next.js provides robust failure recovery via `handleHardNavError` and `useNavFailureHandler`. Sources: [packages/next/src/client/components/nav-failure-handler.ts:1-47](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/nav-failure-handler.ts#L1-L47) ```typescript export function handleHardNavError(error: unknown): boolean { if ( typeof window !== 'undefined' && window.next.__pendingUrl && createHrefFromUrl(new URL(window.location.href)) !== createHrefFromUrl(window.next.__pendingUrl) ) { console.error( `Error occurred during navigation, falling back to hard navigation`, error ) window.location.href = window.next.__pendingUrl.toString() return true } return false } ``` Sources: [packages/next/src/client/components/nav-failure-handler.ts:1-47](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/nav-failure-handler.ts#L1-L47) The validation check `createHrefFromUrl(new URL(window.location.href)) !== createHrefFromUrl(window.next.__pendingUrl)` ensures that a hard navigation fallback is only triggered if the current URL differs from the pending navigation target. Sources: [packages/next/src/client/components/nav-failure-handler.ts:1-47](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/nav-failure-handler.ts#L1-L47) When triggered, it logs the navigation error and assigns `window.location.href` to force a full-document reload to recover to a consistent state. Sources: [packages/next/src/client/components/nav-failure-handler.ts:1-47](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/nav-failure-handler.ts#L1-L47) --- ## Development Diagnostics and Segment Explorers During local development (`NODE_ENV !== 'production'`), navigation boundaries integrate with Next.js DevTools and segment explorer overlays to simulate and debug boundary states. Sources: [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx:1-166](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx#L1-L166) The `SegmentStateProvider` and `SegmentBoundaryTriggerNode` allow developers to interactively toggle segment boundary types. Sources: [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx:1-166](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx#L1-L166) | Segment Boundary Type | Trigger Action in DevTools | | :--- | :--- | | `'not-found'` | Renders `NotFoundSegmentNode`, which executes `notFound()` to test 404 boundaries. | | `'error'` | Renders `ErrorSegmentNode`, throwing a simulated error to test `ErrorBoundary`. | | `'loading'` | Renders `LoadingSegmentNode`, suspending indefinitely via a hanging promise. | Sources: [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx:1-166](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx#L1-L166) Furthermore, `AppDevOverlayErrorBoundary` intercepts runtime errors, tracks occurrence via `RuntimeErrorHandler.hadRuntimeError = true`, and invokes `dispatcher.openErrorOverlay()` to present detailed error frames. Sources: [packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx:1-104](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx#L1-L104) --- ## Design Trade-Offs | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | **String Digest Error Signatures** (`NEXT_HTTP_ERROR_FALLBACK;404`) | Enables lightweight cross-boundary type checking without heavy class inheritance or `instanceof` bundle coupling across RSC boundaries. | Relies on string parsing format stability for all internal routing control flows. | | **Granular Segment-Level Boundaries** | Failures in leaf components or route segments are isolated to their local layout branch without breaking parent chrome. | Increases React component tree depth and boundary wrapper overhead across nested route layouts. | | **Hard Navigation Fallback on Pending Failures** | Prevents the application from getting permanently stuck in broken intermediate states during interrupted client routing transitions. | Discards client-side SPA state and forces a full browser reload when soft navigation failures occur. | Sources: [packages/next/src/client/components/http-access-fallback/http-access-fallback.ts:1-62](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/http-access-fallback/http-access-fallback.ts#L1-L62), [packages/next/src/client/components/error-boundary.tsx:1-182](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/error-boundary.tsx#L1-L182), [packages/next/src/client/components/nav-failure-handler.ts:1-47](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/nav-failure-handler.ts#L1-L47) ## Related - [[Client App Router]] --- ## Technical docs: Incremental Cache URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/caching-and-export/incremental-cache
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/lib/patch-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts) - [packages/next/src/server/response-cache/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts) - [packages/next/src/server/lib/incremental-cache/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/index.ts) - [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts) - [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts) - [packages/next/src/client/components/segment-cache/cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts) - [packages/next/src/server/lib/incremental-cache/file-system-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/file-system-cache.ts) - [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts) - [packages/next/src/server/web/spec-extension/revalidate.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/revalidate.ts) - [packages/next/src/server/revalidation-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/revalidation-utils.ts) - [packages/next/src/server/response-cache/types.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/types.ts) - [packages/next/src/server/app-render/instant-validation/instant-validation.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx) - [packages/next/src/server/app-render/collect-segment-data.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/collect-segment-data.tsx) - [packages/next/src/client/dev/debug-channel.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/debug-channel.ts) - [packages/next/src/server/lib/incremental-cache/tags-manifest.external.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/tags-manifest.external.ts) - [packages/next/src/server/use-cache/cache-tag.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/cache-tag.ts) - [packages/next/src/server/lib/dedupe-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dedupe-fetch.ts) - [packages/next/src/server/lib/encode-cache-tag.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/encode-cache-tag.ts) - [packages/next/src/server/route-modules/app-route/module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts) - [packages/next/src/shared/lib/page-path/ensure-leading-slash.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/page-path/ensure-leading-slash.ts) - [packages/next/src/shared/lib/page-path/normalize-page-path.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/page-path/normalize-page-path.ts)
## Overview The Incremental Cache powers Next.js rendering optimization and incremental static regeneration (ISR) by persisting and retrieving prerendered routes, data fetches, and function outputs across requests. It bridges server-side render pipelines and persistent backends—such as the default file system cache or custom cache handlers—to eliminate redundant computation and database queries. By integrating request deduplication, cache tag invalidation, and client segment coordination, the incremental cache subsystem ensures data freshness while maintaining high-performance response streaming. Sources: [packages/next/src/server/lib/incremental-cache/index.ts:83-95](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/index.ts#L83-L95), [packages/next/src/server/lib/incremental-cache/file-system-cache.ts:36-63](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L36-L63) ## IncrementalCache Core Architecture ### Overview The `IncrementalCache` subsystem manages persistent caching across Next.js rendering operations by implementing the `IncrementalCache` and `CacheHandler` interfaces. It coordinates between in-memory caches, disk storage via `FileSystemCache`, and custom pluggable cache handlers. When a cache lookup is requested, the system inspects route metadata, file modification times (`mtime`), and tag expiration states to determine whether a stored entry is fresh, stale, or expired. Sources: [packages/next/src/server/lib/incremental-cache/index.ts:39-81](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/index.ts#L39-L81), [packages/next/src/server/lib/incremental-cache/index.ts:83-105](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/index.ts#L83-L105), [packages/next/src/server/lib/incremental-cache/file-system-cache.ts:36-63](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L36-L63) ### Cache Handler and Core Interfaces The cache architecture relies on explicit TypeScript interfaces that define the contract for retrieving, storing, and revalidating cache entries across different cache kinds. | Interface / Class | Key Members / Methods | Purpose | | :--- | :--- | :--- | | `CacheHandler` | `constructor(ctx)`, `get(key, ctx)`, `set(key, data, ctx)`, `revalidateTag(tags, durations)`, `resetRequestCache()` | Base class defining the mandatory handler contract for custom and built-in cache backends. | | `CacheHandlerContext` | `fs`, `dev`, `flushToDisk`, `serverDistDir`, `maxMemoryCacheSize`, `fetchCacheKeyPrefix`, `prerenderManifest`, `revalidatedTags`, `_requestHeaders` | Configuration context passed to cache handlers upon initialization. | | `IncrementalCache` | `get(cacheKey, ctx)`, `set(key, data, ctx)`, `revalidateTag(tags, durations)` | Concrete implementation fulfilling response and fetch caching requirements. | | `FileSystemCache` | `memoryCache`, `get(...)`, `set(...)`, `revalidateTag(...)` | Default file-system and LRU memory-backed implementation of `CacheHandler`. | Sources: [packages/next/src/server/lib/incremental-cache/index.ts:39-81](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/index.ts#L39-L81), [packages/next/src/server/lib/incremental-cache/file-system-cache.ts:36-63](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L36-L63) ### File System Persistence and Retrieval The `FileSystemCache` manages multi-format assets on disk, distinguishing between `APP_ROUTE`, `APP_PAGE`, `PAGES`, and `FETCH` cache kinds. When retrieving items, it checks the LRU memory cache before falling back to file system reads via `this.fs.readFile` and `this.fs.stat`. ```mermaid sequenceDiagram participant Caller participant IncrementalCache participant FileSystemCache participant LRUMemory as Memory Cache (LRU) participant Disk as File System (fs) Caller->>IncrementalCache: get(cacheKey, ctx) IncrementalCache->>FileSystemCache: get(key, ctx) FileSystemCache->>LRUMemory: get(key) alt Memory Hit LRUMemory-->>FileSystemCache: return cached data else Memory Miss FileSystemCache->>Disk: readFile / stat (HTML, RSC, body, metadata) Disk-->>FileSystemCache: return raw file data & mtime FileSystemCache->>LRUMemory: set(key, data) end FileSystemCache-->>IncrementalCache: return CacheHandlerValue ``` Sources: [packages/next/src/server/lib/incremental-cache/file-system-cache.ts:105-120](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L105-L120), [packages/next/src/server/lib/incremental-cache/file-system-cache.ts:121-296](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L121-L296) ### Design Trade-Offs | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | **LRU Memory Cache layer in front of Disk** | Eliminates redundant I/O operations for frequently accessed fetch and route data. | Consumes heap memory bounded by `maxMemoryCacheSize`. | | **Separate file extensions (`.html`, `.body`, `.rsc`, `.meta`)** | Avoids complex serialization of mixed payloads; allows independent reads of headers and bodies. | Results in multiple file system operations per cached page. | | **Global cache handler symbol resolution (`@next/cache-handlers`)** | Enables external third-party custom cache handlers to be injected globally. | Requires runtime symbol lookup and indirection during instantiation. | Sources: [packages/next/src/server/lib/incremental-cache/index.ts:133-162](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/index.ts#L133-L162), [packages/next/src/server/lib/incremental-cache/file-system-cache.ts:50-63](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L50-L63), [packages/next/src/server/lib/incremental-cache/file-system-cache.ts:121-256](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L121-L256) > [!WARNING] > When `flushToDisk` is disabled or running in edge runtime (`NEXT_RUNTIME === 'edge'`), `FileSystemCache` bypasses disk reads for fetch caches or limits persistence, relying entirely on memory or throwing invariant errors if unexpected route kinds are encountered. Sources: [packages/next/src/server/lib/incremental-cache/file-system-cache.ts:120-121](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L120-L121), [packages/next/src/server/lib/incremental-cache/file-system-cache.ts:157-158](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L157-L158), [packages/next/src/server/lib/incremental-cache/file-system-cache.ts:283-287](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L283-L287) ## Response Cache and Request Batching ### Overview The response cache manages in-memory caching and request deduplication for generated responses. It relies on an LRU storage engine configured via environment variables and uses a batcher utility to ensure that concurrent, identical render requests share a single inflight operation. Sources: [packages/next/src/server/response-cache/index.ts:9-12](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L9-L12), [packages/next/src/server/response-cache/index.ts:53-56](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L53-L56) ### Configuration and LRU Storage In-memory caching behavior is governed by tunable constants parsed from environment variables. These parameters control default Time-To-Live (TTL) values and maximum storage sizes. | Constant | Environment Variable | Default Value | Purpose | | :--- | :--- | :--- | :--- | | `DEFAULT_TTL_MS` | `NEXT_PRIVATE_RESPONSE_CACHE_TTL` | `10000` (10s) | Fallback TTL for cache hit validation when providers omit invocation headers. | | `DEFAULT_MAX_SIZE` | `NEXT_PRIVATE_RESPONSE_CACHE_MAX_SIZE` | `150` | Maximum number of entries stored in the LRU response cache. | Sources: [packages/next/src/server/response-cache/index.ts:44-56](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L44-L56) > [!NOTE] > Compound cache keys combine pathnames and invocation identifiers separated by a null byte (`\0`), ensuring isolation across distinct render invocations. When an invocation identifier is missing, a reserved `__ttl_sentinel__` marker is used. Sources: [packages/next/src/server/response-cache/index.ts:58-68](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L58-L68) ### Request Deduplication via Batching When multiple concurrent callers request the same cache key, duplicate render executions are prevented using a batching mechanism. The call chain routes requests through the cache lookup layer and coordinates revalidations: `handleGet()` → `revalidate()` → `revalidateBatcher.batch()` → `handleRevalidate()` → `responseGenerator()` Sources: [packages/next/src/server/response-cache/index.ts:318-331](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L318-L331), [packages/next/src/server/response-cache/index.ts:418-427](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L418-L427), [packages/next/src/server/response-cache/index.ts:428-444](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L428-L444), [packages/next/src/server/response-cache/index.ts:446-461](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L446-L461) ## Fetch Patching and Request Deduplication ### Overview Next.js intercepts and patches the global `fetch` API during route execution to integrate automatic request deduplication and incremental caching. Global fetch monkey-patching ensures that standard `fetch()` calls executed within server components, route handlers, or page components flow through specialized wrappers capable of tracking metrics, enforcing cache rules, and sharing inflight promises across concurrent calls. Sources: [packages/next/src/server/lib/patch-fetch.ts:427-430](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts#L427-L430), [packages/next/src/server/route-modules/app-route/module.ts:22-22](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts#L22-L22) ### Request Deduplication and Cache Key Generation The deduplication wrapper (`dedupeFetch`) handles incoming request arguments by normalizing string URLs or `Request` instances into a structured cache key via `generateCacheKey`. Requests possessing side effects (such as `POST`, `PUT`, `DELETE`, or `PATCH` methods, or those with `keepalive` enabled) bypass deduplication and execute directly against the original `fetch`. Sources: [packages/next/src/server/lib/dedupe-fetch.ts:44-89](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dedupe-fetch.ts#L44-L89) | Component Property | Included in Dedupe Key? | Purpose / Handling | | :--- | :--- | :--- | | `request.method` | Yes | Differentiates GET, HEAD, and other HTTP operations. | | `request.headers` | Yes (Filtered) | Includes headers excluding distributed tracing entries (`traceparent`, `tracestate`). | | `request.mode` | Yes | Specifies navigation or CORS mode. | | `request.redirect` | Yes | Controls how redirects are handled (`follow`, `error`, `manual`). | | `request.credentials` | Yes | Determines credential inclusion (`omit`, `same-origin`, `include`). | | `request.referrer` | Yes | Specifies the referrer URL. | | `request.referrerPolicy` | Yes | Governs referrer header population. | | `request.integrity` | Yes | Subresource integrity verification hash. | Sources: [packages/next/src/server/lib/dedupe-fetch.ts:10-36](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dedupe-fetch.ts#L10-L36) > [!NOTE] > Passing an explicit `AbortSignal` via `options.signal` acts as an opt-out mechanism for request deduplication, forcing `dedupeFetch` to skip the in-memory cache layer and execute `originalFetch` immediately. Sources: [packages/next/src/server/lib/dedupe-fetch.ts:54-63](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dedupe-fetch.ts#L54-L63) ## Cache Tag Invalidation Pipeline ### Overview The cache tag invalidation pipeline manages on-demand purging of cached data and incremental static regeneration (ISR) state through tag encoding, revalidation triggering, and tags manifest synchronization. When developers invalidate cached data via user-facing functions such as `revalidateTag`, `updateTag`, or `revalidatePath`, inputs are processed and normalized to ensure wire and storage consistency before being dispatched through asynchronous execution boundaries. Sources: [packages/next/src/server/web/spec-extension/revalidate.ts:34-41](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/revalidate.ts#L34-L41), [packages/next/src/server/web/spec-extension/revalidate.ts:49-63](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/revalidate.ts#L49-L63), [packages/next/src/server/web/spec-extension/revalidate.ts:97-123](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/revalidate.ts#L97-L123) ### Tag Encoding and Normalization To prevent Node.js header validation errors (such as `ERR_INVALID_CHAR`) when tag names or route paths contain non-ASCII characters or run-time unicode symbols, tag inputs pass through `encodeCacheTag`. This function evaluates strings against printable ASCII rules and percent-encodes out-of-class character runs while preserving structural separators like commas, forward slashes, and dynamic segment markers. Sources: [packages/next/src/server/lib/encode-cache-tag.ts:1-37](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/encode-cache-tag.ts#L1-L37) Path-based revalidations handled by `revalidatePath` normalize paths by removing trailing slashes, attaching implicit tag prefixes (`NEXT_CACHE_IMPLICIT_TAG_ID`), and appending layout or page types. If a normalized path points to a root or index location, sibling variants are automatically added to the tag array to keep cache references synchronized. Sources: [packages/next/src/server/web/spec-extension/revalidate.ts:97-123](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/revalidate.ts#L97-L123) | Invalidation Function | Context Restriction | Target Profile / Expiration Behavior | | :--- | :--- | :--- | | `revalidateTag` | Outside render / cached functions | Accepts a profile string or `CacheLifeConfig` object (deprecated single argument logs warning). | | `updateTag` | Server Action only | Immediate expiration (`undefined` profile) to enforce read-your-own-writes semantics. | | `revalidatePath` | Outside render / cached functions | Normalizes original path with implicit tags and optional layout/page specifiers. | | `refresh` | Server Action only | Sets `workStore.pathWasRevalidated = ActionDidRevalidateDynamicOnly` on the client. | Sources: [packages/next/src/server/web/spec-extension/revalidate.ts:34-90](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/revalidate.ts#L34-L90), [packages/next/src/server/web/spec-extension/revalidate.ts:97-123](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/revalidate.ts#L97-L123) ### Revalidation Triggering and Execution Pipeline When a revalidation function is invoked, it validates work store state and checks the active `workUnitStore` phase. Calling revalidation methods inside a render phase, cache wrapper, or `generateStaticParams` throws an immediate error or triggers runtime suspension. Valid tags are pushed into `store.pendingRevalidatedTags` and processed via runtime helper wrappers. Sources: [packages/next/src/server/web/spec-extension/revalidate.ts:130-229](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/revalidate.ts#L130-L229) The revalidation flow follows a structured execution sequence managed by lifecycle wrapper routines: `withExecuteRevalidates()` → `cloneRevalidationState()` → `callback()` → `diffRevalidationState()` → `executeRevalidates()` → `revalidateTags()` Sources: [packages/next/src/server/revalidation-utils.ts:6-26](https://github.com/blade47/next.js/blob/main/packages/next/src/server/revalidation-utils.ts#L6-L26), [packages/next/src/server/revalidation-utils.ts:186-221](https://github.com/blade47/next.js/blob/main/packages/next/src/server/revalidation-utils.ts#L186-L221) > [!WARNING] > Invoking `revalidateTag`, `updateTag`, or `revalidatePath` directly during a React component render or inside a `use cache` body will throw an error. Revalidation must always execute outside of renders and cached functions to ensure consistency. Sources: [packages/next/src/server/web/spec-extension/revalidate.ts:137-154](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/revalidate.ts#L137-L154) ### Tags Manifest Synchronization and Expiration Checks The `tagsManifest` map shares state between "use cache" handlers and file-system caches using `TagManifestEntry` definitions. During cache evaluation, `areTagsExpired` and `areTagsStale` inspect manifest values against requested timestamps. Sources: [packages/next/src/server/lib/incremental-cache/tags-manifest.external.ts:3-43](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/tags-manifest.external.ts#L3-L43) ```typescript export interface TagManifestEntry { stale?: number expired?: number } export const tagsManifest = new Map() ``` Sources: [packages/next/src/server/lib/incremental-cache/tags-manifest.external.ts:3-10](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/tags-manifest.external.ts#L3-L10) For immediate expiration checks, `areTagsExpired` calculates current performance times (`performance.timeOrigin + performance.now()`) to determine whether an entry's expiration threshold has elapsed relative to the target cache timestamp. Similarly, `areTagsStale` iterates through tag arrays to verify if recorded stale thresholds exceed baseline generation timestamps. Sources: [packages/next/src/server/lib/incremental-cache/tags-manifest.external.ts:12-43](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/tags-manifest.external.ts#L12-L43) ## Function and Data Cache Wrapping ### Overview Next.js caches and wraps expensive operations, database queries, and component trees through `unstable_cache` and the `'use cache'` directive infrastructure. These wrappers manage execution lifecycles, propagate cache metadata across asynchronous storage boundaries, handle cache invalidation conditions, and enforce timeout or revalidation rules. Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts:56-60](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L56-L60) ### Unstable Cache Execution and Lifecycle Walkthrough When an `unstable_cache`-wrapped function is invoked, it passes through a deterministic sequence of async storage lookups, cache key generation, store configuration, and cache validation checks. The execution flow proceeds as follows: `unstable_cache` invocation (`cachedCb`) → `workAsyncStorage.getStore()` / `workUnitAsyncStorage.getStore()` → `incrementalCache.generateCacheKey()` → `incrementalCache.get()` → `cacheEntry.isStale` check (background revalidation vs. foreground blocking revalidation) → execution via `workUnitAsyncStorage.run(innerCacheStore, cb, ...args)` → `cacheNewResult()` or direct return. Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts:100-310](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L100-L310) > [!WARNING] > Passing an explicit `revalidate: 0` option to `unstable_cache()` throws an immediate invariant error at construction time. Revalidation values must be explicitly set to `false` or a positive number greater than zero (`> 0`). Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts:72-76](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L72-L76) ### Cache Tag Registration and Work Unit Validation The `cacheTag()` utility registers custom tags against the active execution context. It enforces structural checks via `workUnitAsyncStorage`, ensuring that tags are only declared within valid cache scopes and throwing descriptive errors if called incorrectly. Sources: [packages/next/src/server/use-cache/cache-tag.ts:4-32](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/cache-tag.ts#L4-L32) ```typescript export function cacheTag(...tags: string[]): void { if (!process.env.__NEXT_USE_CACHE) { throw new Error( '`cacheTag()` is only available with the `cacheComponents` config.' ) } const workUnitStore = workUnitAsyncStorage.getStore() switch (workUnitStore?.type) { case 'prerender': case 'prerender-client': case 'validation-client': case 'prerender-runtime': case 'prerender-ppr': case 'prerender-legacy': case 'request': case 'unstable-cache': case 'generate-static-params': case undefined: throw new Error( '`cacheTag()` can only be called inside a "use cache" function.' ) case 'cache': case 'private-cache': break default: workUnitStore satisfies never } const validTags = validateTags(tags, '`cacheTag()`') if (!workUnitStore.tags) { workUnitStore.tags = validTags } else { workUnitStore.tags.push(...validTags) } } ``` Sources: [packages/next/src/server/use-cache/cache-tag.ts:4-41](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/cache-tag.ts#L4-L41) > [!NOTE] > Calling `cacheTag()` outside of a valid `'use cache'` function context — such as during standard page requests or inside `generateStaticParams` — triggers an immediate error aborting execution. Sources: [packages/next/src/server/use-cache/cache-tag.ts:13-26](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/cache-tag.ts#L13-L26) ### Cache Discard and Revalidation Evaluation Rules When evaluating whether to serve or discard a cached entry, `shouldDiscardCacheEntry` and `shouldForceRevalidate` inspect implicit tags, recent revalidation flags, draft mode status, and work store headers. Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts:3282-3370](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L3282-L3370) | Evaluation Check | Target Condition | Action Taken on Match | | :--- | :--- | :--- | | `entry.timestamp <= implicitTagsExpiration` | Entry created before implicit tags were revalidated | Discard cache entry (`return true`) | | `isRecentlyRevalidatedTag` | Tag present in `previouslyRevalidatedTags` or `pendingRevalidatedTags` | Discard cache entry (`return true`) | | `workStore.isOnDemandRevalidate` / `isDraftMode` | On-demand revalidation or draft mode active | Force revalidation (`return true`) | | `cache-control === 'no-cache'` (Dev Server) | Request headers specify `no-cache` in dev mode | Force revalidation (`return true`) | Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts:3286-3288](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L3286-L3288), [packages/next/src/server/use-cache/use-cache-wrapper.ts:3293-3293](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L3293-L3293), [packages/next/src/server/use-cache/use-cache-wrapper.ts:3323-3332](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L3323-L3332), [packages/next/src/server/use-cache/use-cache-wrapper.ts:3359-3367](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L3359-L3367) ## Segment Cache and Prerender Hydration ### Overview The client segment cache coordinates how route segments, prefetch streams, and prerender hydration payloads are stored, decoded, and validated. Responses from the server carry specialized byte markers and metadata that dictate whether a stream is partial or complete before ingestion into the cache. Sources: [packages/next/src/client/components/segment-cache/cache.ts:3117-3127](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L3117-L3127), [packages/next/src/client/components/segment-cache/cache.ts:3215-3224](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L3215-L3224) ### Prefetch Stream Processing and Partial Byte Stripping When runtime prefetch streams are received, `processRuntimePrefetchStream` strips leading control bytes using `stripIsPartialByte`, decodes the underlying React Server Components stream, and extracts vary parameters and stale times. Sources: [packages/next/src/client/components/segment-cache/cache.ts:3163-3192](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L3163-L3192) The byte marker prefix inspection follows a strict protocol: | Byte Marker | Hex Value | Interpretation | | :--- | :--- | :--- | | `'#'` | `0x23` | Complete response | | `'~'` | `0x7e` | Partial response | | Unmarked | — | Fallback to `__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS` flag | Sources: [packages/next/src/client/components/segment-cache/cache.ts:3232-3246](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L3232-L3246) > [!NOTE] > Valid RSC Flight rows start with a hex digit or a colon (`:`), ensuring that marker bytes (`#` or `~`) never collide with legitimate Flight data rows. Sources: [packages/next/src/client/components/segment-cache/cache.ts:3217-3220](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L3217-L3220) ### Segment Traversal and Validation Planning On the server side, instant validation builds route trees and traverses payload segments to coordinate validation boundaries and segment request keys. The traversal visits each route node and formats segment path strings using URL encoding conventions. Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:109-126](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L109-L126), [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:182-188](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L182-L188) ```typescript function stringifySegment(segment: Segment): SegmentPath { return ( typeof segment === 'string' ? encodeURIComponent(segment) : encodeURIComponent(segment[0]) + '|' + segment[1] + '|' + segment[2] ) as SegmentPath } ``` Sources: [packages/next/src/server/app-render/instant-validation/instant-validation.tsx:182-188](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx#L182-L188) > [!WARNING] > Unmarked runtime prefetch responses behave differently depending on whether cached navigations are enabled globally; omitting the experimental flag changes response partiality defaults. Sources: [packages/next/src/client/components/segment-cache/cache.ts:3232-3246](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts#L3232-L3246) ## Related - [[Function Caching]] - [[Response Cache]] --- ## Technical docs: Function Caching URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/caching-and-export/function-caching
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts) - [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts) - [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts) - [packages/next/src/server/lib/patch-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts) - [packages/next/src/server/dev/use-cache-probe-worker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts) - [packages/next/src/server/route-modules/app-route/module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts) - [packages/next/src/server/dev/on-demand-entry-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts) - [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts) - [packages/next/src/server/response-cache/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts) - [packages/next/src/lib/coalesced-function.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/coalesced-function.ts) - [packages/next/src/lib/with-promise-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/with-promise-cache.ts) - [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts) - [packages/next/src/server/web/sandbox/sandbox.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts) - [packages/next/src/server/use-cache/use-cache-probe-globals.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-probe-globals.ts) - [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts) - [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts) - [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts)
## Overview Function caching provides robust execution wrapping, key encoding, and response streaming capabilities for `'use cache'` operations within the App Router architecture. It addresses challenges related to concurrent function re-executions, storage isolation, and request context preservation across both Node.js server and Edge runtime environments. Key design decisions involve leveraging LRU-based memory stores, asynchronous worker pools for state exploration, and interoperability layers with legacy caching mechanisms like `unstable_cache`. Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L1619-L1653), [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L61-L71), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L44-L61), [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L63-L122), [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L35-L50) ## Function Cache Wrapper Architecture ### Overview Function caching wraps `'use cache'` invocations to enforce execution constraints, route lookup requests through registered cache handlers, and manage React flight stream caching. When a cached function is invoked, the wrapper validates the active work store context, resolves the appropriate storage backend, and coordinates retrieval or re-execution. Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L1619-L1653), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L97-L110) ### Execution Call-Chain Walkthrough The core retrieval and execution path flows through specific module boundaries during a cache lookup operation. 1. `cache` (`packages/next/src/server/use-cache/use-cache-wrapper.ts`): Intercepts the function call, validates the `workAsyncStorage` and `workUnitStore` contexts, determines whether the function is private or public, constructs cache contexts, and invokes handler resolution. Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L1619-L1653) 2. `getCacheHandler` (`packages/next/src/server/use-cache/handlers.ts`): Queries the global `handlersMapSymbol` map using the specified cache kind (e.g., `'default'` or `'remote'`), throwing an error if cache handlers have not been initialized or returning the requested `CacheHandler` instance. Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L103-L110) 3. `get` (`packages/next/src/server/lib/cache-handlers/default.ts`): Executes on the resolved `CacheHandler` instance, checking pending promises, evaluating LRU memory store entries, validating expiration timestamps or tags (`areTagsExpired`, `areTagsStale`), and teeing the underlying `ReadableStream` for safe consumption. Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L69-L128) ```mermaid sequenceDiagram participant W as cache (use-cache-wrapper.ts) participant H as getCacheHandler (handlers.ts) participant C as get (default.ts) W->>H: getCacheHandler(kind) H-->>W: CacheHandler instance W->>C: handler.get(cacheKey) C-->>W: Cached entry or undefined ``` Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L1619-L1653), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L103-L110), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L69-L128) ### Cache Context and Work Unit States The wrapper inspects the active `workUnitStore.type` to enforce rules regarding where `'use cache'` and `'use cache: private'` expressions can execute. | Work Unit Store Type | Public `'use cache'` Behavior | Private `'use cache: private'` Behavior | | :--- | :--- | :--- | | `prerender` | Executes render tracking and static generation | Returns hanging promise via `makeHangingPromise` | | `prerender-ppr` | Postpones execution via dynamic tracking | Postpones execution via `postponeWithTracking` | | `prerender-legacy` | Throws interrupt static generation | Throws via `throwToInterruptStaticGeneration` | | `prerender-client` / `validation-client` | Throws `InvariantError` (forbidden in client components) | Throws `InvariantError` (forbidden in client components) | | `cache` | Creates nested dynamic cache error context | Throws invalid dynamic usage error | | `unstable_cache` | Allowed / standard nesting | Throws invalid dynamic usage error | | `request` / `prerender-runtime` / `private-cache` | Standard cache context creation | Standard private cache context creation | Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L1698-L1801) > [!WARNING] > Using `'use cache: private'` within an `unstable_cache()` block or a nested public `'use cache'` function throws an `InvalidDynamicUsageError` during execution because private state cannot be safely nested inside shared caching boundaries. > Sources: [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts#L1723-L1738) ### Design Trade-Offs | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | Global symbol storage for cache handlers (`@next/cache-handlers`) | Shares cache handler instances across distinct module copies and boundary boundaries | Relies on global singleton state which complicates isolated unit testing | | Teeing streams on cache hit (`entry.value.tee()`) | Allows concurrent readers to consume independent clones of the cached `ReadableStream` | Increases memory overhead by buffering chunks until both branches are consumed | | Pending promise deduplication map (`pendingSets`) | Prevents cache stampedes and duplicate concurrent writes for the same cache key | Holds promises in memory until concurrent set operations settle | Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L10-L29), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L58-L75), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L113-L115) ## Cache Handler Initialization and Resolution ### Overview Cache handler initialization and resolution manage how Next.js sets up, loads, and routes requests to default or custom cache handlers within Node.js server runtimes. Handlers are stored globally using specific symbols (`@next/cache-handlers`, `@next/cache-handlers-map`, `@next/cache-handlers-set`, and `@next/cache-handlers-private`) to guarantee that identical cache instances remain accessible across different module copies and boundary lines. Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L10-L29) ### Initialization and Custom Handler Loading The initialization sequence sets up global maps and fallback handlers, loading user-defined modules when specified in the runtime configuration. ```mermaid sequenceDiagram participant S as NextNodeServer (next-server.ts) participant RM as RouteModule (route-module.ts) participant H as handlers.ts participant D as default.ts S->>RM: unstable_preloadEntries() / getIncrementalCache() RM->>H: initializeCacheHandlers(cacheMaxMemorySize) H->>D: createDefaultCacheHandler(cacheMaxMemorySize) D-->>H: Default cache handler instance H-->>RM: Initialized maps & sets RM->>H: setCacheHandler(kind, customHandler) ``` Sources: [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts#L433-L471), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L37-L95), [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L294-L338) The call-chain for handler loading proceeds through distinct phases: `constructor` triggers `unstable_preloadEntries`, which calls `loadCustomCacheHandlers`, invoking `initializeCacheHandlers`, followed by `set` and `resolvePending`. 1. `constructor` initiates server lifecycle setup and triggers preloading when not in development mode. Sources: [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L200-L236) 2. `unstable_preloadEntries` awaits `prepare()` and invokes custom cache handlers before preloading components. Sources: [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L294-L302) 3. `loadCustomCacheHandlers` checks `cacheHandlers` configuration and checks whether initialization has occurred. Sources: [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts#L433-L444) 4. `initializeCacheHandlers` allocates the global handlers map and sets up default or symbol-backed handlers. Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L37-L95) 5. `set` (via `setCacheHandler`) populates the cache handlers map and set with resolved custom handlers. Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L161-L173) 6. `resolvePending` settles active promises inside `pendingSets` once storage operations complete. Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L130-L167) > [!WARNING] > Calling `getCacheHandler()`, `getPrivateCacheHandler()`, or `setCacheHandler()` before `initializeCacheHandlers()` has executed will throw an explicit error stating `'Cache handlers not initialized'`. > Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L103-L125), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L161-L168) ### Cache Handler Symbols and Accessors | Symbol Name | Global Key | Purpose | | :--- | :--- | :--- | | `handlersSymbol` | `@next/cache-handlers` | Stores raw pre-existing `RemoteCache` or `DefaultCache` references on `globalThis` | | `handlersMapSymbol` | `@next/cache-handlers-map` | Maps string kinds (such as `'default'` and `'remote'`) to active `CacheHandler` instances | | `handlersSetSymbol` | `@next/cache-handlers-set` | Maintains a unique `Set` of all active `CacheHandler` instances | | `privateHandlerSymbol` | `@next/cache-handlers-private` | Dev-only dedicated in-memory cache handler for private cache entries | Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L10-L29), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L89-L92) > [!NOTE] > The private cache handler is intentionally stored outside the kind-keyed map on `[privateHandlerSymbol]` so that user-configured custom cache handlers can never inadvertently intercept or replace request-specific private cache storage. > Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L112-L125) ### Design Trade-Offs | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | Gating private cache initialization on `process.env.__NEXT_DEV_SERVER` | Avoids persisting request-derived private cache data to shared production stores | Increases dev-mode memory footprint by maintaining a separate LRU instance | | Dynamic ESM imports via `dynamicImportEsmDefault` and `formatDynamicImportPath` | Supports flexible path resolution and interop wrapping for custom user cache handlers | Introduces asynchronous module loading overhead during server preparation | | Bypassing cache initialization when `cacheMaxMemorySize` equals `0` | Eliminates unnecessary LRU cache instantiation and memory allocation | Disables local memory caching entirely for that handler instance | Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L89-L93), [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts#L433-L471), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L44-L56) ## Edge Runtime Cache Handler Integration ### Edge Runtime Cache Handler Integration ### Overview The `EdgeRouteModuleWrapper` class manages route execution specifically within the edge runtime. During an incoming request handling cycle, it initializes cache handlers using configuration retrieved from the route module and binds custom cache handlers before invoking the underlying route module handler. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L31-L105) ```mermaid sequenceDiagram participant EdgeRouteModuleWrapper as EdgeRouteModuleWrapper participant Handlers as initializeCacheHandlers participant SetHandler as setCacheHandler participant DefaultCache as resolvePending EdgeRouteModuleWrapper->>Handlers: handler -> initializeCacheHandlers(nextConfig.cacheMaxMemorySize) Handlers->>SetHandler: set -> setCacheHandler(kind, cacheHandler) SetHandler->>DefaultCache: resolvePending -> resolvePending() ``` Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L101-L104), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L37-L95), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L161-L173), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L130-L167) ### Call-Chain Execution Walkthrough 1. `handler` — The private `handler` method executes upon receiving an incoming `NextRequestHint` and `NextFetchEvent`, fetches edge configuration via `this.routeModule.getNextConfigEdge()`, and calls `initializeCacheHandlers(nextConfig.cacheMaxMemorySize)`. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L84-L101) 2. `initializeCacheHandlers` — Allocates the global handlers map on `globalThis`, seeds default or remote handlers, and configures fallback mechanisms. Sources: [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L37-L95) 3. `set` — Iterates over `this.cacheHandlers` entries within the edge wrapper, invoking `setCacheHandler(kind, cacheHandler)` to bind each custom handler into the global map and set. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L102-L104), [packages/next/src/server/use-cache/handlers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/handlers.ts#L161-L173) 4. `resolvePending` — When cache set operations occur within cache handlers, active `pendingSets` promises are registered and subsequently resolved via `resolvePending()` once stream chunk reading and caching finalize. Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L130-L167) > [!WARNING] > The edge runtime does not support Cache Components or static page generation; `useCacheTimeout` and `staticPageGenerationTimeout` are explicitly set to `0` as sentinels to surface configuration bugs immediately if ever read. > Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L133-L142) ### Wrap Options and Render Configuration | Property | Type | Purpose | | :--- | :--- | :--- | | `page` | `string` | The route page pathname being wrapped | | `cacheHandlers` | `Record` | Optional custom cache handlers bound during edge request initialization | | `incrementalCacheHandler` | `typeof IncrementalCacheHandler` | Optional incremental cache handler class reference passed to the edge adapter | Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L24-L28) ## Default In-Memory Cache Store Implementation ### Overview The default in-memory cache handler provides local, LRU-backed storage for cached function outputs and React Flight streams. When configured with a maximum memory size (`maxSize`), it instantiates an internal `LRUCache` instance that sizes entries based on the byte length of their underlying `ReadableStream` chunks. If `maxSize` is set to `0`, the handler short-circuits to bypass memory allocation entirely, returning resolved `undefined` or no-op promises for all operations. Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L44-L61) ### Pending Promise Resolution and In-Flight Coalescing To prevent duplicate concurrent execution during cache population, the default cache handler tracks active asynchronous writes using a `pendingSets` map keyed by `cacheKey`. When a `set` operation begins, it generates a new `Promise` and stores its resolver in `pendingSets`. Incoming `get` requests check this map and await the pending promise before querying the underlying LRU store, ensuring that concurrent requests coalesce onto a single active write operation. Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L62-L75), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L130-L138) ### Expiration and Tag Tracking Mechanics Cache entry validity is governed by timestamps, age thresholds, and associated cache tags managed via the tags manifest. The `get` implementation evaluates whether an entry has expired based on environment runtime targets: production environments drop entries once they pass their `revalidate` window, whereas development servers (`next dev`) serve stale entries until their absolute `expire` time is reached, relying on stale-while-revalidate wrappers to trigger background refreshing. Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L1-L13), [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L86-L99) > [!NOTE] > Tag validation inspects both `areTagsExpired` and `areTagsStale`. If any associated tag is expired, the cache entry is treated as a cache miss (`undefined`), whereas stale tags downgrade the effective revalidate value to `-1` to signal stale-while-revalidate behavior. > Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L103-L111) ### Cache Handler Interface Implementation | Method | Parameters | Return Type | Purpose | | :--- | :--- | :--- | :--- | | `get` | `cacheKey: string` | `Promise` | Retrieves a cached entry if present, valid, and unexpired, teeing its stream value. | | `set` | `cacheKey: string, pendingEntry: Promise` | `Promise` | Awaits entry resolution, calculates stream byte size, and stores it in the LRU cache. | | `refreshTags` | `tags: string[], soft?: boolean` | `Promise` | No-op method for in-memory cache handlers. | | `getExpiration` | `tags: string[]` | `Promise` | Calculates the most recent expiration timestamp across the provided tags manifest entries. | | `updateTags` | `tags: string[], durations?: { expire?: number }` | `Promise` | Marks specified tags as stale and updates expiration durations in the tags manifest. | Sources: [packages/next/src/server/lib/cache-handlers/default.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/cache-handlers/default.ts#L69-L213) ## Development Probe Pool and Worker Scheduling ### Overview During development, Next.js implements a hang-detection probe pool and worker scheduling architecture to diagnose and resolve deadlocks during `'use cache'` execution. When a cache fill operation stalls or hangs beyond a configured threshold, the main server process dispatches a probe request to an isolated background worker thread or child process. This mechanism uses `jest-worker` to maintain a pool of four workers, ensuring that state exploration does not block the primary request-handling thread. Sources: [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L58-L122), [packages/next/src/server/dev/use-cache-probe-worker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts#L65-L180) ### Probe Worker Initialization and Execution Flow The execution walkthrough for a hang-detection probe spans the main thread pool and the worker module as follows: `installUseCacheProbe()` wires the global hook in `use-cache-probe-globals.ts` → when a fill stalls, `runProbe()` grabs an active pool via `getPool()` → `activePool.probeUseCache(msg)` is called with a serialized `ProbeMessage` → the worker executes `probeUseCache()` in `use-cache-probe-worker.ts`, which sets HTTP agent options, loads components via `loadComponents()`, retrieves the server module map via `getServerModuleMap()`, decodes arguments using `decodeReply()` or `decodeReplyFromAsyncIterable()`, builds a request store via `buildProbeWorkStore()`, and finally runs the wrapped `'use cache'` function inside `workAsyncStorage.run()` and `workUnitAsyncStorage.run()`. Sources: [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L63-L194), [packages/next/src/server/dev/use-cache-probe-worker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts#L65-L180) > [!NOTE] > The probe worker runs without an outer render context. Because of this isolation, cache-scope fetches resolve normally, and the shared module scope can never accumulate a halted promise that would poison sibling probe tasks. > Sources: [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L66-L72) ### Probe Configuration and Worker Options | Option / Parameter | Type | Meaning / Purpose | | :--- | :--- | :--- | | `maxRetries` | `number` | Set to `0` to prevent automatic worker retries upon failure. | | `numWorkers` | `number` | Fixed at `4` concurrent workers to absorb parallel cache fill probes. | | `enableWorkerThreads` | `boolean` | Controlled by `nextConfig.experimental.workerThreads` to toggle worker threads versus child processes. | | `exposedMethods` | `string[]` | Explicitly lists `['probeUseCache']` to bypass parent-process discovery overhead. | | `timeoutMs` | `number` | Configured timeout threshold triggering the deadlock probe. | Sources: [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L99-L121) > [!WARNING] > The dev server drops and tears down the entire worker pool whenever `onCacheInvalidation()` fires (such as during HMR refreshes or route recompilations). Without this teardown, workers would retain stale `require.cache` and manifest bindings from previous code versions. > Sources: [packages/next/src/server/dev/use-cache-probe-worker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts#L71-L79), [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L159-L166) ### Design Trade-Offs | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | **Isolated `jest-worker` pool** | Prevents deadlocked cache fills from freezing the main dev server request thread. | Fixed worker memory footprint and serialization overhead for arguments. | | **Base64 blob encoding** | Ensures binary payload survival across child-process JSON fallback transports and worker threads. | Additional CPU overhead and memory allocation during argument serialization. | | **Coarse pool teardown on HMR** | Guarantees absolute freshness of user modules and module manifests without path-level tracking. | Discards warm worker caches on every file invalidation event. | Sources: [packages/next/src/server/dev/use-cache-probe-worker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-worker.ts#L38-L44), [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L66-L77), [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts#L159-L166) ## Legacy Cache Interoperability and Fetch Patching ### Overview The caching infrastructure interoperates with legacy primitives such as `unstable_cache`, patched native `fetch` operations, and coalesced function invocations. These mechanisms interact through unified incremental cache layers, shared asynchronous storage boundaries, and request stores. Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L100-L135), [packages/next/src/lib/coalesced-function.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/coalesced-function.ts#L10-L41) ### `unstable_cache` Execution and Revalidation The `unstable_cache` wrapper intercepts expensive operations by combining a fixed function key with serialized arguments, fetching from or updating the underlying `IncrementalCache`. Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L61-L138) ```typescript export function unstable_cache( cb: T, keyParts?: string[], options: { revalidate?: number | false tags?: string[] } = {} ): T ``` Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L61-L71) During execution, `unstable_cache` performs the following call-chain sequence: 1. `workAsyncStorage.getStore()` and `workUnitAsyncStorage.getStore()` are queried to retrieve the active request and work unit contexts. Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L100-L103) 2. `incrementalCache.generateCacheKey(invocationKey)` builds the final hashed key from the fixed key and argument string. Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L134-L135) 3. If an App Router store is present, `workStore.incrementalCache` or `globalThis.__incrementalCache` is consulted to retrieve stored entries or schedule background revalidations via `workStore.pendingRevalidates[invocationKey]`. Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L105-L115), [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L284-L296) 4. If the cache entry is missing or invalid, `workUnitAsyncStorage.run(innerCacheStore, cb, ...args)` runs the underlying callback within an `unstable-cache` work unit store, persisting the result using `cacheNewResult()`. Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L143-L153), [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L307-L331) > [!WARNING] > Passing `revalidate: 0` to `unstable_cache()` throws an invariant error; revalidation must be either `false` or a positive number greater than zero. > Sources: [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts#L72-L76) ### Fetch Patching and Response Caching Patched fetchers integrate with work stores and incremental caches to store network response payloads as cached fetch data entries. Sources: [packages/next/src/server/lib/patch-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts#L164-L170), [packages/next/src/server/lib/patch-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts#L262-L265) ```typescript export function createPatchedFetcher( originFetch: Fetcher, { workAsyncStorage, workUnitAsyncStorage }: PatchableModule ): PatchedFetcher ``` Sources: [packages/next/src/server/lib/patch-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts#L262-L265) During dynamic rendering, `createCachedDynamicResponse` clones response streams, converts array buffers into base64-encoded body strings, and associates them with `serverComponentsHmrCache` and incremental cache instances while deduplicating simultaneous set operations through `workStore.pendingRevalidates`. Sources: [packages/next/src/server/lib/patch-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts#L182-L254) ### Coalesced Function Invocations Concurrent identical function executions are deduplicated using `withCoalescedInvoke`, which maintains a global in-memory promise map. Sources: [packages/next/src/lib/coalesced-function.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/coalesced-function.ts#L8-L41) ```typescript export function withCoalescedInvoke any>( func: F ): ( key: string, args: Parameters ) => Promise>>> ``` Sources: [packages/next/src/lib/coalesced-function.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/coalesced-function.ts#L10-L15) When an invocation occurs, `withCoalescedInvoke` checks `globalInvokeCache` for an existing promise mapped to the key. If an entry exists, subsequent callers receive a cloned promise resolving with `isOrigin: false`. If no entry exists, a wrapper executes `func.apply(undefined, args)`, registers the pending promise in `globalInvokeCache`, and deletes the key upon settlement or rejection. Sources: [packages/next/src/lib/coalesced-function.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/coalesced-function.ts#L16-L40) > [!NOTE] > `withCoalescedInvoke` cleans up its internal tracking map immediately upon promise resolution or rejection in both `.then()` and `.catch()` blocks, preventing permanent memory leaks for transient operations. > Sources: [packages/next/src/lib/coalesced-function.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/coalesced-function.ts#L30-L37) ## Related - [[Incremental Cache]] --- ## Technical docs: Response Cache URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/caching-and-export/response-cache
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/use-cache/use-cache-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/use-cache/use-cache-wrapper.ts) - [packages/next/src/server/response-cache/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts) - [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts) - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/client/components/segment-cache/cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/cache.ts) - [packages/next/src/server/lib/patch-fetch.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/patch-fetch.ts) - [packages/next/src/client/dev/debug-channel.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/debug-channel.ts) - [packages/next/src/client/components/router-reducer/fetch-server-response.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/fetch-server-response.ts) - [packages/next/src/server/web/spec-extension/unstable-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/unstable-cache.ts) - [packages/next/src/server/app-render/instant-validation/instant-validation.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/instant-validation/instant-validation.tsx) - [packages/next/src/server/route-modules/pages/pages-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/pages/pages-handler.ts) - [packages/next/src/server/response-cache/web.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/web.ts) - [packages/next/src/server/lib/incremental-cache/file-system-cache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/incremental-cache/file-system-cache.ts) - [packages/next/src/client/components/router-reducer/ppr-navigations.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/router-reducer/ppr-navigations.ts) - [packages/next/src/client/components/segment-cache/bfcache.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/segment-cache/bfcache.ts) - [packages/next/src/server/render-result.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts) - [packages/next/src/server/response-cache/utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/utils.ts) - [packages/next/src/server/app-render/action-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/action-handler.ts) - [packages/next/src/server/request-meta.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request-meta.ts)
## Overview ### Overview Intro The Response Cache subsystem is a server-level architectural component responsible for coordinating, batching, keying, and retaining rendered application payloads and HTML results across requests. In Next.js rendering pipelines (spanning both Pages and App Routers), multiple concurrent requests for the same route can arrive simultaneously. Without centralized control, this concurrency triggers redundant renders, race conditions, and excessive memory utilization. The `ResponseCache` class and its accompanying request-meta utilities solve this by intercepting lookups, wrapping generation callbacks inside specialized batchers (`Batcher.create`), and integrating with underlying incremental persistence layers (`IncrementalCache` or filesystem caches). Sources: [packages/next/src/server/response-cache/index.ts:107-190](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L107-L190) A core design decision in the response cache architecture is the decoupling of cache key generation from raw pathname strings through compound keys. By combining pathnames with invocation identifiers (`invocationID`) or fallback TTL sentinels (`TTL_SENTINEL`), the subsystem prevents conflicting overlapping renders in minimal server modes or on-demand revalidation tasks. Furthermore, the subsystem manages execution flow between volatile memory (`LRUCache`) and persistent backends, ensuring that background revalidations (`waitUntil`) do not block incoming client responses while maintaining strict cache integrity. Sources: [packages/next/src/server/response-cache/index.ts:53-104](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L53-L104), [packages/next/src/server/response-cache/index.ts:138-190](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L138-L190) The subsystem interacts tightly with `RouteModule` dispatchers, `RenderResult` abstractions, and asynchronous storage contexts (`workAsyncStorage`, `workUnitAsyncStorage`). By standardizing how cache entries are parsed, converted (`toResponseCacheEntry`, `fromResponseCacheEntry`), and protected against stampedes, the response cache provides a reliable bridge between dynamic server-side rendering logic and static distribution targets. Sources: [packages/next/src/server/response-cache/utils.ts:14-40](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/utils.ts#L14-L40), [packages/next/src/server/route-modules/route-module.ts:1103-1173](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts#L1103-L1173) --- ## Public API and Interface Surface ### Surface Overview The response cache subsystem exposes class-based interfaces for managing cached route responses and minimal-mode memory entries. The primary implementation is `ResponseCache`, adhering to the `ResponseCacheBase` contract, alongside a lighter `WebResponseCache` implementation for edge/web runtimes lacking persistent incremental cache bindings. Sources: [packages/next/src/server/response-cache/index.ts:107-218](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L107-L218) - `ResponseCache`: Main server-side cache manager. Implements `get()` to coordinate lookup, request batching, and cache population. - `WebResponseCache`: Lightweight map-based response cache used in web runtimes where incremental cache stores are unavailable. - `RenderResult`: Encapsulates response payloads (strings, buffers, or readable streams) along with route metadata (headers, status codes, revalidation controls). Sources: [packages/next/src/server/response-cache/web.ts:8-32](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/web.ts#L8-L32), [packages/next/src/server/render-result.ts:111-201](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts#L111-L201) | Component / Class | Primary Method | Input Parameters | Return Type | Description | | :--- | :--- | :--- | :--- | :--- | | `ResponseCache` | `get` | `key`, `responseGenerator`, `context` | `Promise` | Batches and resolves cached entries or invokes generation. | | `WebResponseCache` | `get` | `key`, `responseGenerator`, `context` | `Promise` | Manages in-memory pending promises for web runtimes. | | `RenderResult` | `toUnchunkedString` | `stream?: boolean` | `string \| Promise` | Converts response streams or buffers into a unified string. | Sources: [packages/next/src/server/response-cache/index.ts:200-218](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L200-L218), [packages/next/src/server/render-result.ts:199-201](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts#L199-L201) --- ## Compound Key Generation and Invalidation Logic Cache lookup keys in `ResponseCache` are not simple pathnames. To support minimal mode and background revalidation tasks without data contamination, the subsystem constructs compound keys joining the route pathname with an invocation identifier or fallback sentinel. Sources: [packages/next/src/server/response-cache/index.ts:85-91](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L85-L91) ```mermaid flowchart TD A["Incoming Request"] --> B{"Has invocationID?"} B -- Yes --> C["Compound Key: pathname + '\0' + invocationID"] B -- No --> D["Compound Key: pathname + '\0' + '__ttl_sentinel__'"] C --> E["LRU Cache Lookup"] D --> E E --> F{"Cache Hit & Valid?"} F -- Yes --> G["Return Cached Response Entry"] F -- No --> H["Batch & Execute Response Generator"] ``` Sources: [packages/next/src/server/response-cache/index.ts:229-260](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L229-L260) The compound key structure uses a null byte (`\0`) separator (`KEY_SEPARATOR`) because null bytes cannot appear in valid URL paths or UUIDs. When `minimal_mode` is enabled, entries are stored in a bounded `LRUCache` instance. Sources: [packages/next/src/server/response-cache/index.ts:59-91](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L59-L91), [packages/next/src/server/response-cache/index.ts:138-190](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L138-L190) > [!NOTE] > When `invocationID` is undefined, the subsystem falls back to `TTL_SENTINEL` (`__ttl_sentinel__`) and validates entries against a configurable Time-To-Live (`DEFAULT_TTL_MS`, defaulting to 10 seconds). Memory pressure is managed via LRU eviction rather than active timers. Sources: [packages/next/src/server/response-cache/index.ts:44-81](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L44-L81) --- ## Request Batching and Concurrency Control To prevent cache stampedes and duplicate render passes when multiple concurrent requests target an uncached route, `ResponseCache` utilizes two internal `Batcher` instances: `getBatcher` and `revalidateBatcher`. Sources: [packages/next/src/server/response-cache/index.ts:108-131](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L108-L131) ```typescript private readonly getBatcher = Batcher.create< { key: string; isOnDemandRevalidate: boolean }, IncrementalResponseCacheEntry | null, string >({ cacheKeyFn: ({ key, isOnDemandRevalidate }) => `${key}-${isOnDemandRevalidate ? '1' : '0'}`, schedulerFn: scheduleOnNextTick, }) ``` Sources: [packages/next/src/server/response-cache/index.ts:108-122](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L108-L122) When `ResponseCache.get()` is invoked: 1. If `key` is null, it bypasses caching entirely and immediately executes the `responseGenerator`. 2. In minimal mode, the LRU cache is checked. If a valid entry exists (or a TTL-valid item is found), it is converted via `toResponseCacheEntry` and returned. 3. Otherwise, the request is passed to `getBatcher.batch()`. The batcher ensures that subsequent lookups with identical keys during the current tick reuse the pending promise. 4. Background revalidations are registered with `waitUntil(promise)` to ensure serverless containers or Node runtimes do not prematurely terminate execution. Sources: [packages/next/src/server/response-cache/index.ts:219-307](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L219-L307) --- ## Call-Chain Execution Walkthrough The following walkthrough traces the verified execution path from route handling through response cache lookup, conversion, static instantiation, and result wrapping (`handleResponse` → `get` → `toResponseCacheEntry` → `fromStatic` → `RenderResult`): Sources: [packages/next/src/server/route-modules/route-module.ts:1111-1171](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts#L1111-L1171), [packages/next/src/server/response-cache/index.ts:200-307](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L200-L307), [packages/next/src/server/response-cache/utils.ts:42-79](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/utils.ts#L42-L79), [packages/next/src/server/render-result.ts:149-158](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts#L149-L158), [packages/next/src/server/render-result.ts:110-170](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts#L110-L170) 1. **Route Module Handler**: `RouteModule.handleResponse()` receives rendering options and calls `responseCache.get(cacheKey, responseGenerator, context)`. 2. **Response Cache Lookup**: `ResponseCache.get()` verifies keys, checks memory caches, or delegates to `handleGet()` via the batcher to load incremental cache payloads. 3. **Entry Transformation**: `toResponseCacheEntry()` transforms stored incremental entries into `ResponseCacheEntry` structures. 4. **Static Instantiation**: `RenderResult.fromStatic()` wraps static string or buffer payloads along with the HTML content type. 5. **Render Result Construction**: The final `RenderResult` instance is returned to the handler for pipeline dispatch. Sources: [packages/next/src/server/route-modules/route-module.ts:1138-1155](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts#L1138-L1155), [packages/next/src/server/response-cache/index.ts:306-307](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L306-L307), [packages/next/src/server/response-cache/utils.ts:42-79](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/utils.ts#L42-L79), [packages/next/src/server/render-result.ts:149-170](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts#L149-L170) ```mermaid sequenceDiagram participant RM as RouteModule participant RC as ResponseCache participant UT as ResponseCacheUtils participant RR as RenderResult RM->>RC: handleResponse() -> responseCache.get() RC-->>UT: toResponseCacheEntry(incrementalEntry) UT->>RR: RenderResult.fromStatic(value, contentType) RR-->>RM: RenderResult instance ``` Sources: [packages/next/src/server/route-modules/route-module.ts:1138-1171](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts#L1138-L1171), [packages/next/src/server/response-cache/utils.ts:42-79](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/utils.ts#L42-L79), [packages/next/src/server/render-result.ts:149-158](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts#L149-L158) --- ## Data Structures and Type Coercion The response cache converts internal cache records between storage formats and runtime render results using conversion utility functions. Sources: [packages/next/src/server/response-cache/utils.ts:1-40](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/utils.ts#L1-L40) - `ResponseCacheEntry`: Runtime representation containing `RenderResult` instances (`html`), headers, status codes, and `CacheControl` metadata. - `IncrementalResponseCacheEntry`: Serialized representation where HTML and RSC payloads are stored as unchunked strings or binary buffers (`Buffer`). Sources: [packages/next/src/server/response-cache/index.ts:1-10](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L1-L10) | Utility Function | Source Type | Target Type | Conversion Purpose | | :--- | :--- | :--- | :--- | | `fromResponseCacheEntry` | `ResponseCacheEntry` | `IncrementalResponseCacheEntry` | Prepares runtime HTML/RSC streams for disk/cache serialization by unchunking strings. | | `toResponseCacheEntry` | `IncrementalResponseCacheEntry` | `ResponseCacheEntry` | Wraps raw serialized strings/buffers into `RenderResult` instances for execution. | | `routeKindToIncrementalCacheKind` | `RouteKind` | `IncrementalCacheKind` | Maps route module kinds to their corresponding incremental cache storage folder/type. | Sources: [packages/next/src/server/response-cache/utils.ts:14-99](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/utils.ts#L14-L99) > [!CAUTION] > Dynamic responses cannot be unchunked synchronously. Attempting to call `toUnchunkedString()` on an active stream without specifying `stream: true` will throw an `InvariantError`. Sources: [packages/next/src/server/render-result.ts:202-219](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render-result.ts#L202-L219) --- ## Error Handling and Eviction Edge Cases The response cache subsystem implements specific guardrails for memory exhaustion, eviction monitoring, and missing cache entries: Sources: [packages/next/src/server/response-cache/index.ts:142-190](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L142-L190) - **Eviction Tracking**: When the internal `LRUCache` evicts an entry in minimal mode, the eviction listener extracts the `invocationID` from the compound key and registers it in `evictedInvocationIDs` (bounded to 100 entries). If a subsequent request matches an evicted invocation ID, `warnOnce` logs a warning advising the developer to increase `NEXT_PRIVATE_RESPONSE_CACHE_MAX_SIZE`. - **Invariant Enforcement**: If a cache key is provided during a response handling pass, but no cache entry is returned and revalidation-only-generated checks do not bail out, the server throws an invariant error: `'invariant: cache entry required but not generated'`. - **Background Error Handling**: In `WebResponseCache`, errors thrown during background revalidation are caught and logged if the response promise has already resolved, preventing unhandled rejections from crashing the worker. Sources: [packages/next/src/server/response-cache/index.ts:145-190](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/index.ts#L145-L190), [packages/next/src/server/route-modules/route-module.ts:1157-1171](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts#L1157-L1171), [packages/next/src/server/response-cache/web.ts:114-126](https://github.com/blade47/next.js/blob/main/packages/next/src/server/response-cache/web.ts#L114-L126) ## Related - [[Incremental Cache]] - [[Server Request Lifecycle]] --- ## Technical docs: Static Export URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/caching-and-export/static-export
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/export/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/export/index.ts) - [packages/next/src/export/worker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/export/worker.ts) - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/cli/internal/static-routes-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts) - [packages/next/errors.json](https://github.com/blade47/next.js/blob/main/packages/next/errors.json) - [packages/next/src/cli/next-build.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-build.ts) - [packages/next/src/export/utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/export/utils.ts) - [packages/next/src/server/lib/router-utils/filesystem.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/filesystem.ts) - [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts) - [packages/next/src/export/routes/app-page.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-page.ts) - [packages/next/src/server/app-render/collect-segment-data.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/collect-segment-data.tsx) - [packages/next/src/types.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/types.ts) - [packages/next/src/server/render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx) - [packages/next/src/export/routes/app-route.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts) - [packages/next/src/cli/internal/upload-trace.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/upload-trace.ts) - [packages/next/src/shared/lib/constants.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/constants.ts) - [packages/next/src/trace/report/to-json-build.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/trace/report/to-json-build.ts) - [packages/create-next-app/templates/index.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts) - [packages/next/src/export/routes/pages.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/pages.ts) - [packages/next/src/telemetry/events/build.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/events/build.ts) - [packages/next/src/trace/trace-uploader.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/trace/trace-uploader.ts) - [packages/next-codemod/transforms/__testfixtures__/next-lint-to-eslint-cli/legacy-config/package.json](https://github.com/blade47/next.js/blob/main/packages/next/src/export/utils.ts) - [packages/next-codemod/transforms/__testfixtures__/next-lint-to-eslint-cli/flat-config-flat-compat/package.json](https://github.com/blade47/next.js/blob/main/packages/next/src/export/utils.ts) - [packages/next-codemod/transforms/__testfixtures__/next-lint-to-eslint-cli/flat-config-flat-compat-with-other-compat/package.json](https://github.com/blade47/next.js/blob/main/packages/next/src/export/utils.ts) - [packages/next-codemod/transforms/__testfixtures__/next-lint-to-eslint-cli/flat-config/package.json](https://github.com/blade47/next.js/blob/main/packages/next/src/export/utils.ts) - [packages/next/taskfile.js](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js) - [packages/next-swc/package.json](https://github.com/blade47/next.js/blob/main/packages/next-swc/package.json) - [packages/next/src/server/app-render/blocking-route-messages.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/blocking-route-messages.ts) - [apps/bundle-analyzer/package.json](https://github.com/blade47/next.js/apps/bundle-analyzer/package.json) - [Cargo.toml](https://github.com/blade47/next.js/Cargo.toml)
## Overview Static Export is a core Next.js build subsystem responsible for translating an application's compiled build artifacts into fully pre-rendered static assets, HTML pages, Server Components payloads (`.rsc`), and data files (`.json`) that can be hosted directly on any static web server or CDN without needing a running Node.js server. When configured with `output: export` or executed via export routines, Next.js shifts route processing completely to build time, transforming dynamic page routes, app directory layout paths, and API handlers into deterministic file trees. Sources: [packages/next/src/export/index.ts:192-244](https://github.com/blade47/next.js/blob/main/packages/next/src/export/index.ts#L192-L244) The subsystem bridges the gap between server-side rendering pipelines and static file distribution by leveraging production manifests (`pages-manifest.json`, `app-path-routes-manifest.json`, `prerender-manifest.json`), mocking HTTP requests and responses, and executing rendering workers. It enforces strict structural rules, rejecting incompatible server APIs like `getServerSideProps` or unoptimized image loaders, and serializes page outputs via utility writers into precise filesystem hierarchies. Sources: [packages/next/src/export/utils.ts:3-14](https://github.com/blade47/next.js/blob/main/packages/next/src/export/utils.ts#L3-L14), [packages/next/src/export/index.ts:259-284](https://github.com/blade47/next.js/blob/main/packages/next/src/export/index.ts#L259-L284) ## Export Lifecycle and Initialization Control Flow The static export process initializes inside `exportAppImpl`, orchestrating environment setup, manifest loading, output directory clearance, and route dispatching. Before any page compilation occurs, the subsystem loads the user configuration using `PHASE_EXPORT`, verifies the existence of the production `BUILD_ID` file, and parses manifests to discover available pages and app routes. Sources: [packages/next/src/export/index.ts:192-235](https://github.com/blade47/next.js/blob/main/packages/next/src/export/index.ts#L192-L235) ```mermaid flowchart TD A["exportAppImpl(dir, options)"] --> B["Load dotenv & Next Config
(PHASE_EXPORT)"] B --> C["Validate BUILD_ID existence
in distDir"] C --> D["Read pagesManifest & appRoutePathManifest"] D --> E["Clear outDir & create
_next/[buildId] directory"] E --> F["Copy static directories
(public & .next/static)"] F --> G["Iterate routes and dispatch
to worker export pipelines"] ``` Sources: [packages/next/src/export/index.ts:192-375](https://github.com/blade47/next.js/blob/main/packages/next/src/export/index.ts#L192-L375) If the `BUILD_ID` file is missing inside the distribution directory, an `ExportError` is thrown, halting the export pipeline to prevent silent failures. Custom configurations and custom routes are audited; if custom headers, rewrites, or redirects are detected outside of Next.js hosting support, warnings are logged. Sources: [packages/next/src/export/index.ts:237-256](https://github.com/blade47/next.js/blob/main/packages/next/src/export/index.ts#L237-L256) > [!NOTE] > The `public` and `static` directories at the project root are reserved in Next.js and cannot be used as the export output directory (`outDir`), triggering an immediate `ExportError` if attempted. Sources: [packages/next/src/export/index.ts:334-347](https://github.com/blade47/next.js/blob/main/packages/next/src/export/index.ts#L334-L347) ## Route Processing Pipeline and Worker Implementation Once initialization completes, routes are dispatched to `exportPageImpl` within the worker subsystem. Each route is normalized based on its directory origin (`app/` or `pages/`), locale configuration, and dynamic parameters. Mock HTTP request and response objects are generated via `createRequestResponseMocks` to simulate runtime execution context. Sources: [packages/next/src/export/worker.ts:71-167](https://github.com/blade47/next.js/blob/main/packages/next/src/export/worker.ts#L71-L167) ```mermaid sequenceDiagram participant Index as exportAppImpl participant Worker as exportPageImpl participant RouteMod as App/Pages Module participant Writer as MultiFileWriter Index->>Worker: Dispatch exportPath & renderOpts Worker->>Worker: Create request/response mocks & params Worker->>RouteMod: Load components & execute render RouteMod-->>Worker: Return HTML, RSC payload, & metadata Worker->>Writer: Append file outputs (.html, .rsc, .json) Writer-->>Worker: Commit to filesystem Worker-->>Index: Return ExportRouteResult ``` Sources: [packages/next/src/export/worker.ts:71-245](https://github.com/blade47/next.js/blob/main/packages/next/src/export/worker.ts#L71-L245) The worker determines filename structures depending on whether `subFolders` (trailing slashes) are configured, formatting paths as either `${p}/index.html` or `${p}.html`. For `app/` routes, the subsystem handles both page components and App Route handlers (`route.ts`), extracting blobs, response headers, status codes, and writing accompanying `.body` and `.meta` files. Sources: [packages/next/src/export/worker.ts:201-245](https://github.com/blade47/next.js/blob/main/packages/next/src/export/worker.ts#L201-L245), [packages/next/src/export/routes/app-route.ts:36-174](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-route.ts#L36-L174) > [!WARNING] > Dynamic App Router pages with unknown parameters or missing static generation parameters will fail static export unless wrapped with proper fallback handling or explicit static generation configuration. Sources: [packages/next/src/export/index.ts:308-330](https://github.com/blade47/next.js/blob/main/packages/next/src/export/index.ts#L308-L330) ## Pages Directory vs. App Directory Export Handling The static export subsystem bifurcates handling depending on whether a route originates from the legacy `pages/` directory or the modern `app/` directory. Sources: [packages/next/src/export/routes/pages.ts:29-57](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/pages.ts#L29-L57), [packages/next/src/export/routes/app-page.ts:40-88](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-page.ts#L40-L88) - **Pages Directory (`exportPagesPage`)**: Renders page components, checks for forbidden hooks like `getServerSideProps` (which throws `SERVER_PROPS_EXPORT_ERROR`), and writes associated `.json` data files into the pages data directory using `NEXT_DATA_SUFFIX`. - **App Directory (`exportAppPage`)**: Executes `lazyRenderAppPage`, handling React Server Component (`.rsc`) payloads, parallel route segments, prefetch hints, and segment data files (`.rsc_segments/`). Sources: [packages/next/src/export/routes/pages.ts:73-137](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/pages.ts#L73-L137), [packages/next/src/export/routes/app-page.ts:95-182](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-page.ts#L95-L182) | Directory / Module | Route Type | Output Files Generated | Restrictions & Invariants | | :--- | :--- | :--- | :--- | | `pages/` | `PAGES` | `.html`, `._next/data/.../*.json` | `getServerSideProps` prohibited | | `pages/` | `PAGES_API` | Skipped / Node.js function | API routes not supported in static export | | `app/` | `APP_PAGE` | `.html`, `.rsc`, `._segments/` | Dynamic data without caching throws bailout | | `app/` | `APP_ROUTE` | `.body`, `.meta` | Must enable static gen or use caching | Sources: [packages/next/src/shared/lib/constants.ts:32-68](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/constants.ts#L32-L68), [packages/next/src/export/routes/pages.ts:48-56](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/pages.ts#L48-L56), [packages/next/src/export/routes/app-page.ts:107-162](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/app-page.ts#L107-L162) ## Configuration and Custom Output Resolution Next.js manages custom export targets through `hasCustomExportOutput`, detecting when `output: export` is configured in `next.config.js`. When this mode is active, `next build` automatically triggers the export phase, mapping the user-configured distribution directory to act as the final output destination while keeping temporary manifests inside `.next`. Sources: [packages/next/src/export/utils.ts:3-14](https://github.com/blade47/next.js/blob/main/packages/next/src/export/utils.ts#L3-L14) ```typescript export function hasCustomExportOutput(config: NextConfigComplete) { return config.output === 'export' && config.distDir !== '.next' } ``` Sources: [packages/next/src/export/utils.ts:3-14](https://github.com/blade47/next.js/blob/main/packages/next/src/export/utils.ts#L3-L14) The CLI build harness (`nextBuild`) initializes build execution flags, manages memory debugging modes via `enableMemoryDebuggingMode` and `disableMemoryDebuggingMode`, and captures CPU profiles upon receiving termination signals (`SIGTERM`, `SIGINT`). Sources: [packages/next/src/cli/next-build.ts:39-150](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-build.ts#L39-L150) ## Error Handling and Static Generation Bailouts Static export enforces rigid boundaries against runtime dynamic data access. When a route attempts to access uncacheable data sources, request metadata, or dynamic APIs (such as `cookies()`, `headers()`, or uncached `fetch()`) outside of a `` boundary during static generation, the render engine throws static generation bailout errors. Sources: [packages/next/src/server/app-render/app-render.tsx:7378-7384](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx#L7378-L7384), [packages/next/src/server/render.tsx:578-614](https://github.com/blade47/next.js/blob/main/packages/next/src/server/render.tsx#L578-L614) These bailouts trigger specific error messages defined in `errors.json`: - **Error Code 553 / 558 / 577**: Triggered when a route configured with `dynamic = "error"` or standard static generation encounters dynamic runtime usage without caching or fallback generation. - **Error Code 603**: Triggered when Image Optimization uses the default loader during export, requiring either `next start` or `images.unoptimized = true` in `next.config.js`. Sources: [packages/next/errors.json:554-612](https://github.com/blade47/next.js/blob/main/packages/next/errors.json#L554-L612), [packages/next/src/server/app-render/blocking-route-messages.ts:1-27](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/blocking-route-messages.ts#L1-L27) > [!CAUTION] > Utilizing `getServerSideProps` or unoptimized default image loaders will immediately abort the static export process with fatal build errors. Sources: [packages/next/src/export/routes/pages.ts:48-50](https://github.com/blade47/next.js/blob/main/packages/next/src/export/routes/pages.ts#L48-L50), [packages/next/errors.json:604](https://github.com/blade47/next.js/blob/main/packages/next/errors.json#L604) ## Static Routes Analysis and Telemetry Tracking Post-export analysis can be performed using the internal static routes CLI (`staticRoutesInfoCli`). This utility parses built artifacts statically without executing the application code, partitioning per-route file footprints into six distinct categories: 1. `clientJs`: Client-side JavaScript bundles. 2. `clientCss`: Client stylesheet assets. 3. `clientMaps`: Client-side source maps. 4. `serverBundled`: Bundled server code artifacts. 5. `serverUnbundled`: Unbundled server dependencies. 6. `serverMaps`: Server-side source maps. Sources: [packages/next/src/cli/internal/static-routes-info.ts:1-70](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L1-L70) Concurrently, build metrics and feature usage are recorded via telemetry events (`eventBuildCompleted`, `eventBuildOptimize`, and `eventBuildFeatureUsage`) to track static page ratios, bundler usage (Webpack, Turbopack, or Rspack), and experimental feature adoption. Sources: [packages/next/src/telemetry/events/build.ts:76-180](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/events/build.ts#L76-L180) ## Related - [[Incremental Cache]] - [[CLI Commands]] --- ## Technical docs: Edge Sandbox Context URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/edge-and-sandbox/edge-sandbox-context
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/web/sandbox/context.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts) - [packages/next/src/server/web/sandbox/sandbox.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts) - [packages/next/src/server/app-render/app-render.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/app-render.tsx) - [packages/next/src/server/patch-error-inspect.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts) - [packages/next/src/server/node-environment-extensions/error-inspect.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/node-environment-extensions/error-inspect.tsx) - [packages/next/src/next-devtools/userspace/app/forward-logs.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/forward-logs.ts) - [packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts) - [packages/next/src/server/web/globals.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/globals.ts) - [packages/next/src/next-devtools/userspace/app/errors/intercept-console-error.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/intercept-console-error.ts) - [packages/next/src/client/react-client-callbacks/error-boundary-callbacks.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/react-client-callbacks/error-boundary-callbacks.ts) - [packages/next/src/server/node-environment-extensions/unhandled-rejection.external.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/node-environment-extensions/unhandled-rejection.external.tsx) - [packages/next/src/server/web/sandbox/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/index.ts) - [packages/next/src/next-devtools/userspace/app/app-dev-overlay-setup.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/app-dev-overlay-setup.ts) - [packages/next/src/next-devtools/userspace/pages/pages-dev-overlay-setup.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/pages/pages-dev-overlay-setup.tsx) - [packages/next/src/next-devtools/userspace/app/errors/stitched-error.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/stitched-error.ts) - [packages/next/src/client/react-client-callbacks/on-recoverable-error.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/react-client-callbacks/on-recoverable-error.ts) - [packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx) - [packages/next/src/client/dev/runtime-error-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/runtime-error-handler.ts) - [packages/next/src/client/react-client-callbacks/report-global-error.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/react-client-callbacks/report-global-error.ts) - [packages/next/src/next-devtools/userspace/app/errors/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/index.ts) - [packages/next/src/client/components/unstable-rethrow.browser.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/unstable-rethrow.browser.ts) - [packages/next/src/next-devtools/userspace/app/client-entry.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/client-entry.tsx) - [packages/next/src/client/components/unstable-rethrow.server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/unstable-rethrow.server.ts) - [packages/next/src/client/components/unstable-rethrow.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/unstable-rethrow.ts) - [packages/next/src/server/dev/node-stack-frames.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/node-stack-frames.ts) - [packages/next/src/shared/lib/turbopack/internal-error.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/internal-error.ts) - [packages/next/src/next-devtools/dev-overlay/container/runtime-error/index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/container/runtime-error/index.tsx) - [packages/next/src/server/create-deduped-by-callsite-server-error-logger.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/create-deduped-by-callsite-server-error-logger.ts) - [packages/next/src/client/components/hooks-server-context.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/hooks-server-context.ts) - [packages/next/src/client/components/handle-isr-error.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/components/handle-isr-error.tsx)
## Overview ### Overview The Edge Sandbox Context subsystem provides a controlled virtual machine execution environment for running Edge Runtime functions, middleware, and API routes within Next.js. Because Edge functions execute in a restricted environment modeled on standard Web APIs rather than full Node.js server environments, Next.js implements a specialized module context loader and V8-backed runtime using `next/dist/compiled/edge-runtime`. This setup bridges user code with isolated global bindings, simulated environment variables, polyfilled Node.js built-in modules, and resource cleanup managers. Sources: [packages/next/src/server/web/sandbox/context.ts:30-56](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L30-L56) To maintain strict security boundaries and API limitations, the sandbox implements controlled stubbing for unsupported Node.js features and intercepts global execution states. When developers import unsupported modules (such as `fs` or `net`), proxy wrappers dynamically throw unsupported API errors. Sources: [packages/next/src/server/web/globals.ts:57-82](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/globals.ts#L57-L82) Furthermore, the sandbox hooks into error stack formatting, runtime error inspection, and development overlays to ensure that execution traces inside the edge context map correctly back to source code locations. Sources: [packages/next/src/server/patch-error-inspect.ts:350-467](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts#L350-L467) ```mermaid flowchart TD A["Runner Request"] --> B["getRuntimeContext()"] B --> C["getModuleContext()"] C --> D["Initialize EdgeRuntime"] D --> E["Apply Process Polyfills & Global Bindings"] E --> F["Evaluate Module Paths"] F --> G["Execute Edge Handler Function"] G --> H["FetchEventResult Response"] ``` Sources: [packages/next/src/server/web/sandbox/sandbox.ts:72-109](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L72-L109) --- ## Module Context Management and Caching The sandbox architecture caches initialized module contexts to avoid repeated compilation overhead across incoming requests. Module contexts are stored globally in `moduleContexts` (a `Map`) and `pendingModuleCaches` (a `Map>`). A `ModuleContext` interface combines the compiled `EdgeRuntime` instance, a map of loaded paths, and a set of warned evaluations. Sources: [packages/next/src/server/web/sandbox/context.ts:30-58](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L30-L58) When file changes or hot-reloads occur, `clearModuleContext(path: string)` inspects active and pending module caches. If a cached module context contains the modified file path, the entry is evicted, and associated timer resources managed by `intervalsManager` and `timeoutsManager` are purged via `.removeAll()`. Sources: [packages/next/src/server/web/sandbox/context.ts:78-98](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L78-L98) Similarly, `clearAllModuleContexts()` resets all active timers and clears both caches completely. Sources: [packages/next/src/server/web/sandbox/context.ts:63-68](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L63-L68) ```mermaid flowchart LR A["clearModuleContext(path)"] --> B["intervalsManager.removeAll()"] B --> C["timeoutsManager.removeAll()"] C --> D{"Check moduleContexts"} D -->|Match path| E["moduleContexts.delete(key)"] D -->|No match| F{"Check pendingModuleCaches"} F -->|Match path| G["pendingModuleCaches.delete(key)"] ``` Sources: [packages/next/src/server/web/sandbox/context.ts:78-98](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L78-L98) --- ## Environment Variables and Process Polyfilling Edge runtime functions do not have direct access to the host Node.js `process` object. To provide seamless compatibility with standard environment access patterns, Next.js constructs a specialized process polyfill using `createProcessPolyfill(env)`. Sources: [packages/next/src/server/web/sandbox/context.ts:137-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L137-L140) The polyfill merges `process.env` with injected custom environments via `buildEnvironmentVariablesFrom(injectedEnvironments)`, explicitly appending `NEXT_RUNTIME: 'edge'`. Sources: [packages/next/src/server/web/sandbox/context.ts:118-127](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L118-L127) For all other properties on the native `process` object (excluding `env`), `Object.defineProperty` is used to intercept property access. If user code attempts to invoke a property that is a function (e.g., `process.nextTick` or `process.cwd()`), a getter throws an unsupported API error referencing `process.${key}`. Sources: [packages/next/src/server/web/sandbox/context.ts:141-158](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L141-L158) Properties can also be dynamically overridden by assigning values to `processPolyfill`. Sources: [packages/next/src/server/web/sandbox/context.ts:153-155](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L153-L155) | Process Property / API | Polyfill Behavior | Purpose / Restriction | |-----------------------|-------------------|----------------------| | `process.env` | Merged via `buildEnvironmentVariables` | Exposes runtime environment variables plus `NEXT_RUNTIME: 'edge'` | | Function properties | Getter returns function throwing unsupported API error | Prevents unauthorized execution of Node.js-only process methods | | Non-function properties | Returns `undefined` unless overridden | Safeguards against undefined host state leakage | Sources: [packages/next/src/server/web/sandbox/context.ts:118-160](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L118-L160) --- ## Unsupported Module Stubs and API Guards When user code running inside the Edge Sandbox imports or invokes forbidden Node.js APIs or built-in modules, Next.js enforces strict restrictions via `throwUnsupportedAPIError` and `__import_unsupported`. Sources: [packages/next/src/server/web/sandbox/context.ts:129-135](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L129-L135), [packages/next/src/server/web/globals.ts:57-61](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/globals.ts#L57-L61) The `__import_unsupported` function returns a specialized Proxy object. Any attempt to access properties (other than `.then`), construct instances, or invoke the proxy function triggers an immediate error citing the unsupported Node.js module name. Sources: [packages/next/src/server/web/globals.ts:63-82](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/globals.ts#L63-L82) Similarly, `addStub` attaches property getters to the `EdgeRuntime` context that invoke `throwUnsupportedAPIError(name)` when accessed. Sources: [packages/next/src/server/web/sandbox/context.ts:162-171](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L162-L171) > [!CAUTION] > Importing restricted Node.js core modules (such as `fs`, `net`, or `child_process`) in Edge runtime files will throw an error at runtime unless guarded by conditional environment checks. Sources: [packages/next/src/server/web/globals.ts:57-61](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/globals.ts#L57-L61) --- ## Runtime Execution Pipeline and Request Handling The execution lifecycle of an Edge handler is orchestrated by the `run` function in `sandbox.ts`, wrapped with `withTaggedErrors` in development mode to decorate errors with `edge-server` compiler tags. Sources: [packages/next/src/server/web/sandbox/sandbox.ts:49-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L49-L70), [packages/next/src/server/web/sandbox/sandbox.ts:111-163](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L111-L163) The execution sequence proceeds through the following steps: 1. `getRuntimeContext(params)` retrieves or initializes the module context, exposes shared caches (`__incrementalCache`, `__serverComponentsHmrCache`, `NEXT_CLIENT_ASSET_SUFFIX`), and evaluates requested module paths into the V8 context. Sources: [packages/next/src/server/web/sandbox/sandbox.ts:72-109](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L72-L109) 2. Module resolution extracts the default export handler from `runtime.context._ENTRIES`. Sources: [packages/next/src/server/web/sandbox/sandbox.ts:114-116](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L114-L116) 3. Request body streams are cloned if the HTTP method is outside `['HEAD', 'GET']`. Sources: [packages/next/src/server/web/sandbox/sandbox.ts:118-120](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L118-L120) 4. Context wrapping runs the handler inside `edgeSandboxNextRequestContext` and `requestStore` asynchronous local storage providers, mapping headers and setting up request metadata. Sources: [packages/next/src/server/web/sandbox/sandbox.ts:134-156](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L134-L156) ```mermaid sequenceDiagram participant Client as Client Request participant Run as run() / withTaggedErrors participant Context as getRuntimeContext() participant Runtime as EdgeRuntime Sandbox participant Handler as Edge Handler Client->>Run: Invoke runner with params & request data Run->>Context: getRuntimeContext(params) Context->>Runtime: Evaluate module paths & bind globals Runtime-->>Context: Initialized runtime instance Context-->>Run: Ready runtime Run->>Handler: Execute middleware/edge function Handler-->>Run: FetchEventResult (Response + WaitUntil) Run->>Client: Return sanitized response ``` Sources: [packages/next/src/server/web/sandbox/sandbox.ts:72-163](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L72-L163) --- ## Error Inspection and Stack Frame Patching To ensure stack traces originating inside the Edge sandbox or Node.js server environments provide accurate source maps and developer-friendly formatting, Next.js implements error inspection patching via `packages/next/src/server/patch-error-inspect.ts`. Sources: [packages/next/src/server/patch-error-inspect.ts:350-542](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts#L350-L542) The error inspection subsystem overrides `Error.prepareStackTrace` with `prepareUnsourcemappedStackTrace` and attaches custom inspection symbols (`nodejs.util.inspect.custom` for Node.js environments and `edge-runtime.inspect.custom` for edge-lite runtimes). Sources: [packages/next/src/server/patch-error-inspect.ts:502-540](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts#L502-L540) During error serialization or inspection, `parseAndSourceMap` extracts `error.stack`, strips internal React stack frames past `react_stack_bottom_frame` or `react-stack-bottom-frame`, and parses stack frames. Sources: [packages/next/src/server/patch-error-inspect.ts:350-376](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts#L350-L376) It resolves sourcemapped frames using `getSourcemappedFrameIfPossible` against cached source maps, filters anonymous sandwich frames, and rebuilds formatted stacks. Sources: [packages/next/src/server/patch-error-inspect.ts:398-421](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts#L398-L421) ```mermaid flowchart TD A["Error Thrown / Inspected"] --> B["Custom inspect symbol triggered"] --> C["parseAndSourceMap()"] C --> D["Extract stack & truncate internal frames"] --> E["Parse stack frames"] E --> F["Map original positions via source maps"] --> G["Filter ignore-listed sandwich frames"] G --> H["Rebuild formatted stack with code frames"] --> I["Return decorated error string"] ``` Sources: [packages/next/src/server/patch-error-inspect.ts:350-467](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts#L350-L467) --- ## Log Forwarding and Dev Overlay Integration Development errors and console logs captured within edge and server runtimes are forwarded to the client browser or development overlay via `packages/next/src/next-devtools/userspace/app/forward-logs.ts` and `use-error-handler.ts`. Sources: [packages/next/src/next-devtools/userspace/app/forward-logs.ts:88-130](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/forward-logs.ts#L88-L130), [packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts:22-46](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts#L22-L46) The logging subsystem maintains a `logQueue` that batches log entries (`any-logged-error`, `console`, `formatted-error`) and schedules non-blocking transmission (`scheduleLogSend`) using `requestAnimationFrame` and `setTimeout` (`afterThisFrame`). Sources: [packages/next/src/next-devtools/userspace/app/forward-logs.ts:88-130](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/forward-logs.ts#L88-L130) When unhandled errors or rejections occur, `forwardUnhandledError` captures uncaught errors, extracts owner stacks using `getErrorStackWithOwnerStack` (backed by React owner stack tracing in `stitched-error.ts`), and queues log entries with source type designations. Sources: [packages/next/src/next-devtools/userspace/app/forward-logs.ts:373-383](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/forward-logs.ts#L373-L383) Additionally, `handleConsoleError` intercepts console error arguments, parses environment names, wraps errors using `createConsoleError`, and dispatches them asynchronously through microtask queues. Sources: [packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts:22-46](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts#L22-L46) --- ## Native Module Mapping and WebAssembly Loading The sandbox provides explicit polyfills for supported Node.js core modules through `NativeModuleMap`, granting safe subset access to standard APIs. Sources: [packages/next/src/server/web/sandbox/context.ts:191-214](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L191-L214) Supported modules mapped in `NativeModuleMap` include `'node:buffer'`, `'node:events'`, and `'node:async_hooks'`. Sources: [packages/next/src/server/web/sandbox/context.ts:191-214](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L191-L214) Additionally, WebAssembly bindings associated with edge functions are compiled asynchronously into `WebAssembly.Module` instances via `loadWasm`, reading asset files from disk and mapping them by binding name. Sources: [packages/next/src/server/web/sandbox/context.ts:100-116](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L100-L116) | Native Module Key | Exposed APIs / Exports | Underlying Implementation Source | |-------------------|------------------------|----------------------------------| | `node:buffer` | constants, kMaxLength, kStringMaxLength, Buffer, SlowBuffer | BufferImplementation (`node:buffer`) | | `node:events` | EventEmitter, captureRejectionSymbol, defaultMaxListeners, errorMonitor, listenerCount, on, once | EventsImplementation (`node:events`) | | `node:async_hooks` | AsyncLocalStorage, AsyncResource | AsyncHooksImplementation (`node:async_hooks`) | Sources: [packages/next/src/server/web/sandbox/context.ts:191-214](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L191-L214) ## Related - [[Web Spec Adapters]] - [[Middleware Execution]] --- ## Technical docs: Web Spec Adapters URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/edge-and-sandbox/web-spec-adapters
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/web/spec-extension/adapters/next-request.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts) - [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts) - [packages/next/src/server/web/adapter.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts) - [packages/next/src/server/base-http/helpers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/helpers.ts) - [packages/next/src/server/base-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts) - [packages/next/src/server/web/exports/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/exports/index.ts) - [packages/next/src/server/base-http/web.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts) - [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts) - [packages/next/src/server/base-http/node.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts) - [packages/next/src/server/route-modules/app-route/module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts) - [packages/next/src/server/web/spec-extension/request.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/request.ts) - [packages/next/server.js](https://github.com/blade47/next.js/blob/main/packages/next/server.js) - [packages/next/src/server/web/spec-extension/response.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/response.ts) - [packages/next/server.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/server.d.ts) - [packages/next/src/server/web/spec-extension/fetch-event.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/fetch-event.ts) - [packages/next/src/server/send-response.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/send-response.ts) - [packages/next/src/server/web/spec-extension/cookies.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/cookies.ts) - [packages/next/src/server/web/spec-extension/adapters/reflect.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/reflect.ts) - [packages/next/src/server/after/builtin-request-context.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/builtin-request-context.ts) - [packages/next/src/experimental/testmode/server-edge.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testmode/server-edge.ts) - [packages/next/src/experimental/testmode/context.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testmode/context.ts) - [packages/next/src/experimental/testmode/server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testmode/server.ts) - [packages/next/src/server/web/globals.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/globals.ts) - [packages/next/src/server/base-http/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/index.ts) - [packages/next/src/server/web/spec-extension/adapters/headers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts) - [packages/next/src/next-devtools/server/middleware-response.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/server/middleware-response.ts) - [packages/next/src/server/web/sandbox/context.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts) - [packages/next/src/server/web/spec-extension/url-pattern.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/url-pattern.ts) - [packages/next/src/server/app-render/stream-ops.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.ts) - [packages/next/src/api/server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts)
## Overview ### Overview Sub-Section Web Spec Adapters form the bridging layer in Next.js that unifies native Node.js HTTP primitives (`IncomingMessage`, `ServerResponse`) and Web Standard Fetch API primitives (`Request`, `Response`, `Headers`, `URL`) across dual execution runtimes: the traditional Node.js server runtime and the lightweight Edge runtime. Because Next.js features such as Middleware, App Route handlers, and Server Actions expose standard Web API interfaces (`NextRequest`, `NextResponse`), the server infrastructure requires a robust translation subsystem to wrap, normalize, and unwrap requests and responses irrespective of where code executes. Sources: [packages/next/src/server/web/spec-extension/adapters/next-request.ts:1-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L1-L151) ### Purpose and Mechanics The architecture bridges the mismatch between Node.js event-emitter based stream mechanics and WHATWG Fetch streaming specifications. It achieves this by introducing runtime HTTP wrappers (`NodeNextRequest`, `NodeNextResponse`, `WebNextRequest`, `WebNextResponse`), request adapters (`NextRequestAdapter`), and spec-extension classes (`NextRequest`, `NextResponse`) equipped with proxy-based casing preservation, cookie interception, and abort signal tracking. Sources: [packages/next/src/server/base-http/node.ts:1-170](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L1-L170), [packages/next/src/server/base-http/web.ts:1-141](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L1-L141) ### Cross-Runtime Integration By decoupling application handlers from platform-specific IO objects, Web Spec Adapters allow user code to consume standard Web APIs while giving Next.js fine-grained control over header casing, stream consumption tracking, request lifecycle abortion, and telemetry context propagation. Sources: [packages/next/src/server/web/spec-extension/adapters/next-request.ts:1-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L1-L151) ## Architecture of Runtime HTTP Abstractions ### Request and Response Wrappers Next.js defines runtime-specific HTTP representations in `packages/next/src/server/base-http/node.ts` and `packages/next/src/server/base-http/web.ts`. In the Node.js runtime, `NodeNextRequest` wraps `IncomingMessage` and provides streaming conversion via `stream()`, while `NodeNextResponse` wraps `ServerResponse` to manage headers, status codes, and completion listeners. In the Edge runtime, `WebNextRequest` and `WebNextResponse` manage standard `Request`, `TransformStream`, and `Response` objects. Sources: [packages/next/src/server/base-http/node.ts:19-169](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L19-L169), [packages/next/src/server/base-http/web.ts:11-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L11-L140) ### Runtime Abstraction Diagram ```mermaid classDiagram class NodeNextRequest { +IncomingMessage _req +IncomingHttpHeaders headers +FetchMetric[] fetchMetrics +stream() ReadableStream +get originalRequest() } class NodeNextResponse { +ServerResponse _res +number statusCode +string statusMessage +setHeader(name, value) +appendHeader(name, value) +getHeader(name) +send() +onClose(callback) } class WebNextRequest { +Request request +IncomingHttpHeaders headers +FetchMetrics fetchMetrics +parseBody() } class WebNextResponse { +TransformStream transformStream +number statusCode +string statusMessage +setHeader(name, value) +appendHeader(name, value) +toResponse() Promise~Response~ +onClose(callback) } class NextRequest { +RequestCookies cookies +NextURL nextUrl +string url } class NextResponse { +ResponseCookies cookies +static json() +static redirect() +static rewrite() +static next() } NodeNextRequest ..> NextRequest : adapted by NextRequestAdapter WebNextRequest ..> NextRequest : adapted by NextRequestAdapter ``` Sources: [packages/next/src/server/base-http/node.ts:19-169](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L19-L169), [packages/next/src/server/base-http/web.ts:11-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L11-L140), [packages/next/src/server/web/spec-extension/request.ts:14-105](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/request.ts#L14-L105), [packages/next/src/server/web/spec-extension/response.ts:36-153](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/response.ts#L36-L153) ### Runtime Bifurcation The concrete implementations are selected according to environment checks defined in `packages/next/src/server/base-http/helpers.ts`. When `process.env.NEXT_RUNTIME === 'edge'`, type guards `isWebNextRequest` and `isWebNextResponse` return `true`. Otherwise, `isNodeNextRequest` and `isNodeNextResponse` evaluate to `true` for Node.js execution. Sources: [packages/next/src/server/base-http/helpers.ts:18-49](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/helpers.ts#L18-L49) ## NextRequestAdapter and Request Translation ### Adapter Purpose The `NextRequestAdapter` class in `packages/next/src/server/web/spec-extension/adapters/next-request.ts` converts runtime request representations into `NextRequest` instances suitable for route handlers, middleware, and server rendering. Sources: [packages/next/src/server/web/spec-extension/adapters/next-request.ts:56-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L56-L151) ### Request Translation Flow Diagram ```mermaid flowchart TD A["Incoming Request"] --> B{"process.env.NEXT_RUNTIME === 'edge'?"} B -->|Yes| C["NextRequestAdapter.fromWebNextRequest(req)"] B -->|No| D["NextRequestAdapter.fromNodeNextRequest(req, signal)"] C --> E["Return NextRequest (Web stream body)"] D --> F["Attach signalFromNodeResponse & Undici/Node body"] F --> E ``` Sources: [packages/next/src/server/web/spec-extension/adapters/next-request.ts:56-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L56-L151) ### Execution Pipeline Details 1. **Selection:** `NextRequestAdapter.fromBaseNextRequest()` evaluates `process.env.NEXT_RUNTIME === 'edge'` alongside `isWebNextRequest` and `isNodeNextRequest` to branch between Edge and Node conversion logic. 2. **Method & Body Inspection:** GET and HEAD requests explicitly drop body initialization (`let body = null`) per HTTP specifications. For Node requests, non-GET/HEAD bodies are passed directly with `duplex: 'half'`. 3. **URL Resolution:** If `request.url` is absolute, a `URL` object is instantiated directly. If relative, metadata (`initURL` from request meta) or a dummy base (`http://n`) is supplied to construct a valid absolute `URL`. 4. **Abort Signal Binding:** `signalFromNodeResponse()` inspects the underlying writable response. If already errored or destroyed, an aborted signal is immediately returned; otherwise, an `AbortController` listens for the `close` event on the Node `ServerResponse`. If `close` fires before `response.writableFinished`, a `ResponseAborted` error aborts the controller. > [!WARNING] > Request bodies cannot be passed if the associated abort signal has already been triggered; doing so raises a `Request body was disturbed` error in the underlying Fetch implementation. Sources: [packages/next/src/server/web/spec-extension/adapters/next-request.ts:16-54](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L16-L54), [packages/next/src/server/web/spec-extension/adapters/next-request.ts:80-150](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L80-L150) ## HeadersAdapter and Casing Preservation ### Casing Mismatch Problem Standard WHATWG `Headers` objects lowercase all header names. However, Node.js and proxy environments frequently require preserving or querying original casing, or interfacing directly with raw `IncomingHttpHeaders` objects without expensive cloning. Sources: [packages/next/src/server/web/spec-extension/adapters/headers.ts:28-60](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L28-L60) ### HeadersAdapter Proxy Mechanism `HeadersAdapter` in `packages/next/src/server/web/spec-extension/adapters/headers.ts` solves this via a JavaScript `Proxy`. The proxy intercepts `get`, `set`, `has`, and `deleteProperty` traps, normalizes incoming property lookups to lower case, and searches the underlying Node `headers` keys to find the original casing: ```typescript const original = Object.keys(headers).find( (o) => o.toLowerCase() === lowercased ) ``` Sources: [packages/next/src/server/web/spec-extension/adapters/headers.ts:36-115](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L36-L115) ### Class Hierarchy and Sealing ```mermaid classDiagram class Headers { +get(name) +set(name, value) +has(name) +delete(name) } class HeadersAdapter { -IncomingHttpHeaders headers +static seal(headers) ReadonlyHeaders +static from(headers) Headers } Headers <|-- HeadersAdapter ``` Sources: [packages/next/src/server/web/spec-extension/adapters/headers.ts:28-160](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L28-L160) ### Enforcing Read-Only Semantics If found, operations execute against the original casing key. Furthermore, `HeadersAdapter.seal()` wraps a `Headers` instance in a proxy that intercepts mutating methods (`append`, `delete`, `set`) and immediately invokes `ReadonlyHeadersError.callable()` to enforce read-only semantics for server components and cached contexts. Sources: [packages/next/src/server/web/spec-extension/adapters/headers.ts:8-18](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L8-L18), [packages/next/src/server/web/spec-extension/adapters/headers.ts:116-134](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L116-L134) ## NextResponse and Middleware Field Interception ### NextResponse Extensions `NextResponse` extends the global Web `Response` class, adding helper static methods (`json`, `redirect`, `rewrite`, `next`) and embedded cookie/middleware interception logic. Sources: [packages/next/src/server/web/spec-extension/response.ts:36-108](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/response.ts#L36-L108) ### Middleware Field Interception Flow ```mermaid flowchart TD A["NextResponse.redirect / rewrite / next"] --> B["Build Headers with Location / x-middleware-*"] B --> C["handleMiddlewareField()"] C --> D["Extract init.request.headers"] D --> E["Set x-middleware-request-{key} and x-middleware-override-headers"] E --> F["Return new NextResponse"] ``` Sources: [packages/next/src/server/web/spec-extension/response.ts:12-29](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/response.ts#L12-L29) ### Request Header Overrides When custom request headers are modified via middleware response initializers (`MiddlewareResponseInit`), `handleMiddlewareField()` iterates over provided request headers, prefixes them with `x-middleware-request-`, and sets `x-middleware-override-headers` so downstream server layers can reconstruct modified request states. > [!NOTE] > When `NextResponse.redirect()` is invoked, the status code is validated against a strict set of redirect codes (`301`, `302`, `303`, `307`, `308`). Passing an unsupported status code throws a `RangeError`. Sources: [packages/next/src/server/web/spec-extension/response.ts:109-153](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/response.ts#L109-L153) ## Response Sending and Pipe Execution ### Transmission Pipeline Once a route handler or middleware returns a standard `Response` object, `sendResponse()` in `packages/next/src/server/send-response.ts` transmits the payload across the underlying Node.js HTTP transport. Sources: [packages/next/src/server/send-response.ts:14-25](https://github.com/blade47/next.js/blob/main/packages/next/src/server/send-response.ts#L14-L25) ### Sequence Diagram of sendResponse ```mermaid sequenceDiagram participant Server as NextServer participant Sender as sendResponse() participant Res as NodeNextResponse participant NodeRes as http.ServerResponse Server->>Sender: sendResponse(req, res, response, waitUntil) Sender->>Res: Set statusCode & statusText loop For each response header alt Header is set-cookie Sender->>Res: splitCookiesString() & appendHeader() else Multiple values allowed or not present Sender->>Res: appendHeader() end end alt Request method !== HEAD and has body Sender->>NodeRes: pipeToNodeResponse(response.body, originalResponse, waitUntil) else HEAD request or no body Sender->>NodeRes: originalResponse.end() end ``` Sources: [packages/next/src/server/send-response.ts:26-84](https://github.com/blade47/next.js/blob/main/packages/next/src/server/send-response.ts#L26-L84) ### Header Normalization Rules `sendResponse` handles multi-value header normalization explicitly. While most headers overwrite existing values or check presence, headers with multiple values allowed (`set-cookie`, `www-authenticate`, `proxy-authenticate`, `vary`) are split using `splitCookiesString()` and appended individually to the underlying Node `ServerResponse`. Sources: [packages/next/src/server/send-response.ts:35-67](https://github.com/blade47/next.js/blob/main/packages/next/src/server/send-response.ts#L35-L67) ## Sandbox Context and Edge Runtime Adaptations ### Edge Sandbox Environment In the Edge Runtime sandbox (`packages/next/src/server/web/sandbox/context.ts`), global objects such as `fetch`, `Request`, and `Response` are patched to inject Next.js-specific capabilities like inline asset fetching, `next` fetch options configuration, and validation checks. Sources: [packages/next/src/server/web/sandbox/context.ts:401-420](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L401-L420) ### Code Snippet of Edge Request Patching ```typescript const __Request = context.Request context.Request = class extends __Request { next?: NextFetchRequestConfig | undefined constructor(input: URL | RequestInfo, init?: RequestInit | undefined) { const url = typeof input !== 'string' && 'url' in input ? input.url : String(input) validateURL(url) super(input, init) this.next = init?.next } } ``` Sources: [packages/next/src/server/web/sandbox/context.ts:401-420](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L401-L420) ### Unsupported API Stubbing Furthermore, unsupported Node.js APIs (such as filesystem or cluster modules) are stubbed via `__import_unsupported()`, throwing informative runtime errors when invoked inside Edge functions. Sources: [packages/next/src/server/web/sandbox/context.ts:57-82](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L57-L82) ## Runtime Execution & Adapter Integration Table ### Adapter Mapping Table The following reference table outlines the correspondence between core Web Spec Adapter classes, their underlying transport objects, and their target execution runtimes. | Adapter Class | Underlying Transport / Source | Target Runtime | Primary Responsibility | | :--- | :--- | :--- | :--- | | `NodeNextRequest` | `http.IncomingMessage` | Node.js Server | Wraps Node request stream with body streaming and meta tracking. | | `NodeNextResponse` | `http.ServerResponse` | Node.js Server | Wraps Node response socket and manages header mutation. | | `WebNextRequest` | Fetch `Request` / `NextRequestHint` | Edge Runtime | Wraps Web standard requests for edge handlers. | | `WebNextResponse` | `TransformStream` | Edge Runtime | Manages web stream transformations and translates to standard `Response`. | | `HeadersAdapter` | `IncomingHttpHeaders` | Universal | Bridges Node headers with Web `Headers` API preserving casing. | Sources: [packages/next/src/server/base-http/node.ts:19-169](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L19-L169), [packages/next/src/server/base-http/web.ts:11-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L11-L140), [packages/next/src/server/web/spec-extension/adapters/headers.ts:28-231](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L28-L231) ## Related - [[Edge Sandbox Context]] - [[Route Handlers]] --- ## Technical docs: Middleware Execution URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/edge-and-sandbox/middleware-execution
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/web/sandbox/sandbox.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts) - [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts) - [packages/next-routing/src/resolve-routes.ts](https://github.com/blade47/next-routing/src/resolve-routes.ts) - [packages/next/src/server/lib/router-utils/resolve-routes.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts) - [packages/next/src/server/dev/next-dev-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts) - [packages/next/src/server/base-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts) - [packages/next/src/server/dev/hot-reloader-webpack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts) - [packages/next/src/server/web/adapter.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts) - [packages/next/src/server/lib/router-utils/filesystem.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/filesystem.ts) - [packages/next/src/shared/lib/router/router.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/router.ts) - [packages/next/src/server/web/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts) - [packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts) - [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts) - [packages/next/src/server/lib/router-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-server.ts) - [packages/next/src/server/route-modules/app-route/module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts) - [packages/next-routing/src/middleware.ts](https://github.com/blade47/next-routing/src/middleware.ts) - [packages/next-codemod/transforms/__testfixtures__/middleware-to-proxy/runtime-multiple-exports.input.ts](https://github.com/blade47/next-codemod/transforms/__testfixtures__/middleware-to-proxy/runtime-multiple-exports.input.ts) - [packages/next/src/experimental/testing/server/middleware-testing-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testing/server/middleware-testing-utils.ts) - [packages/next/src/server/dev/middleware-webpack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts) - [packages/next/src/server/api-utils/get-cookie-parser.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/get-cookie-parser.ts) - [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts) - [packages/next/src/shared/lib/router/utils/prepare-destination.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts)
## Overview Middleware execution in Next.js governs how incoming HTTP requests are intercepted, evaluated, and transformed before reaching core page handlers or router logic. By running custom code at the edge prior to request completion, it enables dynamic routing decisions, header manipulation, authentication checks, and URL rewrites or redirects based on runtime conditions. Sources: [packages/next/src/server/next-server.ts:1611-1617](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1611-L1617), [packages/next/src/server/web/adapter.ts:377-380](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L377-L380) ## Route Matching and Has Conditions ### Overview Incoming request evaluation relies on matching paths against compiled regular expressions and verifying optional declarative conditions such as headers, cookies, query parameters, and host names. The routing subsystem processes these checks sequentially to determine whether middleware should execute for a given request. Sources: [packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts:14-40](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts#L14-L40), [packages/next/src/shared/lib/router/utils/prepare-destination.ts:48-125](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts#L48-L125) ### Route Matcher Execution Flow The route matching engine executes a deterministic call chain when testing an incoming request against configured route matchers. 1. `unstable_doesMiddlewareMatch()` initializes the validation context, parsing the target URL and constructing request abstractions with headers and cookies. Sources: [packages/next/src/experimental/testing/server/middleware-testing-utils.ts:19-40](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testing/server/middleware-testing-utils.ts#L19-L40) 2. `getMiddlewareRouteMatcher()` iterates over the array of compiled proxy matchers, invoking `RegExp.exec()` against the request pathname. Sources: [packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts:22-26](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts#L22-L26) 3. If the pathname matches, `matchHas()` evaluates any associated `has` or `missing` conditions against the request headers, cookies, query, or host. Sources: [packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts:28-33](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts#L28-L33), [packages/next/src/shared/lib/router/utils/prepare-destination.ts:48-125](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts#L48-L125) 4. If all conditions succeed, the matcher returns `true`; otherwise, iteration continues across remaining matchers or returns `false`. Sources: [packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts:30-38](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts#L30-L38) > [!NOTE] > If a matcher configuration omits the `matcher` property entirely, `unstable_doesMiddlewareMatch()` immediately returns `true` without evaluating path regexes or conditional rules. > Sources: [packages/next/src/experimental/testing/server/middleware-testing-utils.ts:32-34](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testing/server/middleware-testing-utils.ts#L32-L34) ### Condition Evaluation Types The `matchHas` function inspects request properties based on explicit condition types defined in route configurations. | Condition Type | Source Property Evaluated | Value Extraction & Normalization | | --- | --- | --- | | `header` | `req.headers[key]` | Key is lowercased; header value retrieved as string | | `cookie` | `req.cookies` or `req.headers` | Cookies parsed via `getCookieParser()` if `cookies` property is absent on request | | `query` | `query[key]` | Direct lookup against search parameter entries | | `host` | `req.headers.host` | Hostname extracted by splitting at port `:` and lowercasing | Sources: [packages/next/src/shared/lib/router/utils/prepare-destination.ts:56-86](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts#L56-L86) > [!WARNING] > When evaluating `cookie` conditions against raw Node `IncomingMessage` objects, `matchHas` dynamically invokes `getCookieParser()` on request headers rather than relying on pre-parsed cookie collections. > Sources: [packages/next/src/shared/lib/router/utils/prepare-destination.ts:66-72](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts#L66-L72) ### Design Trade-Offs in Route Matching | Design Choice | Benefit | Cost | | --- | --- | --- | | Sequential array iteration over compiled RegExp matchers | Simple implementation and predictable evaluation order | Linear O(n) performance scaling with the number of configured matchers | | Dynamic cookie parser invocation on raw requests | Supports both Web API request objects and Node HTTP `IncomingMessage` instances | Repeated header parsing overhead when multiple cookie conditions are evaluated | | Strict `has` and `missing` boolean conjunction (`every` and `!some`) | Expressive declarative conditions for advanced routing logic | Short-circuiting stops parameter collection early if any secondary constraint fails | Sources: [packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts:22-36](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts#L22-L36), [packages/next/src/shared/lib/router/utils/prepare-destination.ts:66-75](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts#L66-L75), [packages/next/src/shared/lib/router/utils/prepare-destination.ts:117-119](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts#L117-L119) ## Routing Pipeline and Invocation Determination ### Overview When an incoming HTTP request traverses the Next.js server routing pipeline, the system determines whether to execute middleware by matching request paths against compiled route criteria and inspecting internal routing metadata. The process evaluates explicit path matchers, handles data route normalization, and orchestrates target resolution before invoking the server request handler. Sources: [packages/next-routing/src/resolve-routes.ts:492-555](https://github.com/blade47/next-routing/src/resolve-routes.ts#L492-L555), [packages/next/src/server/lib/router-utils/resolve-routes.ts:423-490](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L423-L490) ### Middleware Invocation Determination Walkthrough The decision to trigger middleware for a given request flows through a series of specific checks: 1. `shouldInvokeMiddlewareForRequest()` or `resolveRoutes()` inspects whether `middlewareMatchers` are defined; if `undefined`, legacy behavior defaults to returning `true`. Sources: [packages/next-routing/src/resolve-routes.ts:492-534](https://github.com/blade47/next-routing/src/resolve-routes.ts#L492-L534) 2. If `middlewareMatchers` is an empty array, it immediately short-circuits and returns `false`. Sources: [packages/next-routing/src/resolve-routes.ts:535-537](https://github.com/blade47/next-routing/src/resolve-routes.ts#L535-L537) 3. The raw `url.pathname` is tested against each matcher's compiled regular expression and conditional `has` or `missing` rules. Sources: [packages/next-routing/src/resolve-routes.ts:498-527](https://github.com/blade47/next-routing/src/resolve-routes.ts#L498-L527) 4. If the raw pathname fails to match, the system attempts to decode the pathname using `decodeURIComponent()`. If decoding throws an error, it returns `false`. Sources: [packages/next-routing/src/resolve-routes.ts:543-549](https://github.com/blade47/next-routing/src/resolve-routes.ts#L543-L549) 5. If the decoded pathname differs from the raw pathname, matchers are re-evaluated against the decoded variant; otherwise, it returns `false`. Sources: [packages/next-routing/src/resolve-routes.ts:550-554](https://github.com/blade47/next-routing/src/resolve-routes.ts#L550-L554) > [!WARNING] > Pathname decoding errors during middleware invocation checks are treated as non-fatal and immediately abort matching for that candidate, falling back to `false` rather than throwing a request-level exception. > Sources: [packages/next-routing/src/resolve-routes.ts:546-548](https://github.com/blade47/next-routing/src/resolve-routes.ts#L546-L548) ### Routing Pipeline Metadata and Request Handlers During client transitions and server routing resolution, request metadata flags control how paths and data requirements are interpreted. The `getMiddlewareData` function inspects response headers to manage internal rewrites and redirection states. | Response Header / Meta Key | Type / Source | Purpose | | --- | --- | --- | | `x-nextjs-rewrite` | Response Header | Specifies an internal rewrite target path from middleware execution | | `x-nextjs-matched-path` | Response Header | Fallback header used to detect `next.config.js` rewrites when explicit rewrite targets are absent | | `x-nextjs-data` | Request Header | Indicates a Next.js data fetch request (`/_next/data/...`) requiring special routing checks | | `middlewareInvoke` | Request Meta | Boolean flag indicating whether the active request context originated from middleware processing | | `invokePath` | Request Meta | Stores the resolved execution path assigned during `invokeRender()` | Sources: [packages/next/src/shared/lib/router/router.ts:183-199](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/router.ts#L183-L199), [packages/next/src/server/lib/router-server.ts:318-336](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-server.ts#L318-L336) > [!NOTE] > When `x-nextjs-data` is present, if the normalized `invokePath` matches `/404` while middleware matchers are configured, the server short-circuits execution, sets response status `404`, and returns an empty JSON payload `{}` without invoking the full rendering pipeline. > Sources: [packages/next/src/server/lib/router-server.ts:318-326](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-server.ts#L318-L326) ### Design Trade-Offs in Path Resolution and Invocation | Design Choice | Benefit | Cost | | --- | --- | --- | | Dual raw and decoded pathname matching checks | Gracefully handles percent-encoded paths (e.g., Nginx-decoded proxy requests) | Double evaluation overhead against matcher regexes when raw matching fails | | Undefined matcher fallback to `true` | Preserves backwards compatibility for callers without explicit matchers | Implicitly opts all requests into middleware execution if configuration is omitted | | Request meta augmentation via `addRequestMeta()` | Decouples internal routing state from core Node HTTP request/response objects | Relies on mutable request object property mutation across pipeline boundaries | Sources: [packages/next-routing/src/resolve-routes.ts:531-534](https://github.com/blade47/next-routing/src/resolve-routes.ts#L531-L534), [packages/next-routing/src/resolve-routes.ts:543-554](https://github.com/blade47/next-routing/src/resolve-routes.ts#L543-L554), [packages/next/src/server/lib/router-server.ts:333-343](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-server.ts#L333-L343) ## Server Orchestration and Manifest Loading ### Overview Server-side orchestration prior to core request handling relies on manifest loading mechanisms and runtime inspection to locate, verify, and execute middleware before ordinary route handlers run. The `NodeManifestLoader` class uses the build distribution directory and the server directory constant to load compiled JSON manifests from disk. Concurrently, `NextServer` orchestrates the middleware execution pipeline through `runMiddleware`, validating requests, handling on-demand revalidation bypasses, normalizing absolute URLs, and evaluating edge versus Node.js middleware runtimes. Sources: [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts:5-20](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts#L5-L20), [packages/next/src/server/next-server.ts:1617-1760](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1617-L1760) ### Manifest Loading and Execution Call Chain When the server initializes and resolves routing metadata or evaluates route matchers, manifest loaders retrieve structural configuration files from the build output directory. For middleware and routing entries, the call sequence proceeds through manifest lookup and verification functions before request handling commences. The execution call chain for loading manifests and dispatching middleware proceeds through the following sequence: 1. `NodeManifestLoader.load()` — Joins the distribution directory (`distDir`), the server directory constant (`SERVER_DIRECTORY`), and the requested manifest name to locate the file on disk. Sources: [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts:16-20](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts#L16-L20) 2. `NodeManifestLoader.require()` — Safely executes a Node.js `require()` on the constructed file path, returning `null` if the manifest file is absent or fails to load. Sources: [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts:8-14](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts#L8-L14) 3. `runMiddleware()` — Orchestrates runtime request evaluation in `NextServer`, verifying on-demand revalidation status, normalizing request protocols, and querying `this.getMiddleware()` and `this.hasMiddleware()`. Sources: [packages/next/src/server/next-server.ts:1617-1675](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1617-L1675) 4. `this.getEdgeFunctionInfo()` or `this.loadNodeMiddleware()` — Resolves the execution target based on whether edge function metadata exists in the middleware manifest or falls back to Node.js middleware modules. Sources: [packages/next/src/server/next-server.ts:1678-1714](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1678-L1714) 5. `adapterFn()` or `sandbox.run()` — Executes the resolved middleware module via the web adapter or the isolated sandbox runtime, returning headers, cookies, and `waitUntil` promises. Sources: [packages/next/src/server/next-server.ts:1719-1759](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1719-L1759) > [!WARNING] > If `process.env.NEXT_MINIMAL` is set, calling `runMiddleware()` throws an immediate invariant error because minimal mode expects server orchestration to bypass standard middleware runner execution. > Sources: [packages/next/src/server/next-server.ts:1624-1628](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1624-L1628) ### Orchestration Constants and Manifest Parameters The orchestration layer relies on constant paths, environment configuration flags, and request metadata to determine how middleware is loaded and invoked. | Constant / Parameter | Target Value / Type | Purpose / Behavior | | --- | --- | --- | | `SERVER_DIRECTORY` | `packages/next/src/shared/lib/constants.ts` | Subdirectory name (`server`) appended to `distDir` when loading manifests via `NodeManifestLoader` | | `PRERENDER_MANIFEST` | `packages/next/src/server/next-server.ts` | Filename (`prerender-manifest.json`) loaded by `getPrerenderManifest()` for preview and prerender routing data | | `NEXT_MINIMAL` | `process.env.NEXT_MINIMAL` | Environment check that throws an invariant error if `runMiddleware()` is invoked in minimal runtime mode | | `middlewareInvoke` | Request Meta (`base-server.ts` / `next-server.ts`) | Boolean flag checked by `handleCatchallMiddlewareRequest` to verify if a request originates from middleware processing | Sources: [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts:1-18](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts#L1-L18), [packages/next/src/server/next-server.ts:1624-1628](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1624-L1628), [packages/next/src/server/next-server.ts:1801-1805](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1801-L1805), [packages/next/src/server/next-server.ts:1914-1916](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1914-L1916) ### Design Trade-Offs in Server Orchestration | Design Choice | Benefit | Cost | | --- | --- | --- | | Safe `require()` wrapper returning `null` on failure | Prevents server crashes when optional manifest files are missing from the build output | Masks filesystem corruption or missing build artifacts as missing route configurations | | Conditional fallback from edge manifest to Node.js middleware | Supports legacy or custom Node-based middleware handlers not recorded in `middleware-manifest.json` | Introduces branching complexity and divergent execution paths between edge and Node runtimes | | On-demand revalidation header short-circuiting | Bypasses unnecessary middleware execution for revalidation requests, optimizing build-cache purges | Requires duplicate header parsing logic (`checkIsOnDemandRevalidate`) inside the middleware runner | Sources: [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts:8-14](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts#L8-L14), [packages/next/src/server/next-server.ts:1630-1640](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1630-L1640), [packages/next/src/server/next-server.ts:1712-1721](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1712-L1721) ## Web Adapter and Response Handling ### Overview The web adapter layer converts standard Node HTTP request structures and route module handlers into Fetch API-compatible abstractions (`NextRequest`, `NextResponse`, `NextFetchEvent`), while parsing and validating the response objects returned by middleware execution. Sources: [packages/next/src/server/web/adapter.ts:1-10](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L1-L10), [packages/next/src/server/web/edge-route-module-wrapper.ts:71-82](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L71-L82) ### Call-Chain Execution Walkthrough The execution of a middleware request through the adapter pipeline flows through specific phases from wrapping to response parsing: 1. `EdgeRouteModuleWrapper.wrap()` initializes the module wrapper and binds the wrapper's private `handler()` method as an `EdgeHandler`. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:61-82](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L61-L82) 2. `adapter()` receives request options, sets up the outer `waitUntil` promise context via `getBuiltinRequestContext()`, and instantiates `NextFetchEvent`. Sources: [packages/next/src/server/web/adapter.ts:241-250](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L241-L250) 3. The request is processed by `propagator()`, which checks if `params.page` corresponds to middleware (`/middleware`, `/src/middleware`, `/proxy`, or `/src/proxy`). If true, it initiates a tracer span (`MiddlewareSpan.execute`), creates request and work stores via `createRequestStoreForAPI()` and `createWorkStore()`, and executes `workAsyncStorage.run()` wrapping `params.handler`. Sources: [packages/next/src/server/web/adapter.ts:254-346](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L254-L346) 4. `EdgeRouteModuleWrapper.handler()` normalizes dynamic route parameters via `utils.normalizeDynamicRouteParams()`, instantiates a `CloseController`, calls `this.routeModule.handle(request, context)`, and attaches stream tracking via `trackStreamConsumed()`. Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:84-176](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L84-L176) 5. `responseToMiddlewareResult()` converts the returned middleware `Response` object into a mutable `MiddlewareResult`, processing header overrides (`x-middleware-override-headers`), rewrites (`x-middleware-rewrite`), redirects (`location`), and refresh signals (`x-middleware-refresh`). Sources: [packages/next-routing/src/middleware.ts:13-204](https://github.com/blade47/next-routing/src/middleware.ts#L13-L204) > [!CAUTION] > If the value returned from `params.handler` is truthy but fails to satisfy `response instanceof Response`, the adapter immediately throws a `TypeError('Expected an instance of Response to be returned')`, halting request propagation. > Sources: [packages/next/src/server/web/adapter.ts:362-365](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L362-L365) ### Response Parsing and Header Transformation Tables The response handling logic inspects specific custom headers to determine how request headers and downstream routing destinations are mutated. | Middleware Header / Property | Action / Transformation | Target Header / Result Field | | --- | --- | --- | | `x-middleware-override-headers` | Splits comma-separated keys, deletes request headers not present in the override set, and applies values prefixed with `x-middleware-request-` | `requestHeaders` mutations | | `x-middleware-rewrite` | Parses destination URL, normalizes against base URL, and sets internal routing headers | `x-middleware-rewrite`, `x-nextjs-rewrite`, `result.rewrite` | | `location` | Validates status against allowed redirect status codes (`301`, `302`, `303`, `307`, `308`), converts URL format, and records redirect status | `location`, `result.redirect` | | `x-middleware-refresh` | Sets explicit body sent flag when no rewrite, next, or location header is present | `result.bodySent = true` | | `x-middleware-set-cookie` | Appends or sets cookie values specifically onto request headers | `requestHeaders` | Sources: [packages/next-routing/src/middleware.ts:36-196](https://github.com/blade47/next-routing/src/middleware.ts#L36-L196) ### Adapter Design Trade-Offs | Design Choice | Benefit | Cost | | --- | --- | --- | | Explicit `Response` instance validation | Catches invalid middleware return types early with a descriptive `TypeError` before header parsing runs | Fails the request execution abruptly if middleware developers return plain objects or strings instead of `NextResponse` | | Deferred body-close dispatch via `setTimeout` / `CloseController` | Allows asynchronous task registration via `waitUntil` to complete before request storage and stream resources are torn down | Delays resource cleanup until the next event loop turn, complicating deterministic testing of stream lifetimes | | Automatic relative URL conversion via `getRelativeURL()` | Normalizes internal redirects and rewrites to relative paths when origins match, avoiding cross-domain routing errors | Requires extra `URL` parsing overhead for every outgoing rewrite or redirect header | Sources: [packages/next/src/server/web/adapter.ts:347-365](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L347-L365), [packages/next/src/server/web/edge-route-module-wrapper.ts:159-174](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L159-L174), [packages/next-routing/src/middleware.ts:141-189](https://github.com/blade47/next-routing/src/middleware.ts#L141-L189) ## Sandbox Runtime and Context Evaluation ### Overview Executing edge middleware within isolated runtime environments requires constructing dedicated module contexts, managing asynchronous execution scopes, and stripping restricted transport headers. The sandbox architecture coordinates runtime initialization, request body stream cloning, and error stack-frame mapping for development mode. Sources: [packages/next/src/server/web/sandbox/sandbox.ts:1-163](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L1-L163) ### Execution Walkthrough and Context Initialization The sandbox entry point orchestrates runtime setup and handler invocation through a sequence of discrete operations. 1. `getRuntimeContext(params)` calls `getModuleContext()` to acquire the compiled edge runtime and evaluation helper, attaches incremental caching (`__incrementalCache`, `__incrementalCacheShared`), router server context symbols, HMR caches (`__serverComponentsHmrCache`), and client asset tokens (`NEXT_CLIENT_ASSET_SUFFIX`), then evaluates all provided module paths. Sources: [packages/next/src/server/web/sandbox/sandbox.ts:72-109](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L72-L109) 2. `run(params)` wraps the execution flow using `withTaggedErrors`, invokes `getRuntimeContext(params)` to retrieve the runtime, and extracts the default export function from `runtime.context._ENTRIES['middleware_' + params.name]`. Sources: [packages/next/src/server/web/sandbox/sandbox.ts:49-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L49-L70), [packages/next/src/server/web/sandbox/sandbox.ts:111-116](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L111-L116) 3. Request body streams are inspected via `['HEAD', 'GET'].includes(params.request.method)`. For methods carrying payloads, `params.request.body?.cloneBodyStream()` duplicates the stream. Sources: [packages/next/src/server/web/sandbox/sandbox.ts:118-120](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L118-L120) 4. `edgeSandboxNextRequestContext.run()` and `requestStore.run()` establish the async local storage contexts before executing the `edgeFunction` with a normalized request object that wraps cloned streams via `requestToBodyStream()`. Sources: [packages/next/src/server/web/sandbox/sandbox.ts:134-155](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L134-L155) 5. `FORBIDDEN_HEADERS` are systematically deleted from the resulting response object, and the request body stream is finalized in a `finally` block. Sources: [packages/next/src/server/web/sandbox/sandbox.ts:23-27](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L23-L27), [packages/next/src/server/web/sandbox/sandbox.ts:152-162](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L152-L162) ### Sandbox Configuration and Forbidden Headers | Constant / Parameter | Target Value / Type | Purpose / Behavior | | --- | --- | --- | | `ErrorSource` | `Symbol('SandboxError')` | Identifies errors originating specifically within the sandbox execution environment | | `FORBIDDEN_HEADERS` | `['content-length', 'content-encoding', 'transfer-encoding']` | Restricted transport headers stripped from edge function responses before propagation | | `NEXT_CLIENT_ASSET_SUFFIX` | String (e.g., `?dpl=`) | Global asset suffix injected into `globalThis` when a client asset token is present | | `__incrementalCache` | Cache instance | Shared incremental cache reference attached to the edge runtime global scope | Sources: [packages/next/src/server/web/sandbox/sandbox.ts:21-27](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L21-L27), [packages/next/src/server/web/sandbox/sandbox.ts:84-87](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L84-L87), [packages/next/src/server/web/sandbox/sandbox.ts:100-103](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L100-L103) ### Sandbox Runtime Design Trade-Offs | Design Choice | Benefit | Cost | | --- | --- | --- | | Conditional error tagging via `withTaggedErrors` in development | Maps stack frames to original source locations using `getServerError(error, 'edge-server')` during development | Adds runtime conditional checks and node stack frame module loading overhead in non-production environments | | Explicit `FORBIDDEN_HEADERS` stripping post-execution | Prevents edge functions from malforming underlying transport encoding or content length declarations | Requires traversing and deleting specific header keys on every successful response return | | Request body stream cloning based on HTTP method | Avoids consumption errors on idempotent GET and HEAD requests while preserving payloads for mutating requests | Requires explicit stream finalization logic in a `finally` block to prevent resource leaks | Sources: [packages/next/src/server/web/sandbox/sandbox.ts:49-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L49-L70), [packages/next/src/server/web/sandbox/sandbox.ts:118-120](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L118-L120), [packages/next/src/server/web/sandbox/sandbox.ts:152-162](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L152-L162) > [!WARNING] > If an edge function execution fails to return a response object (resulting in an undefined `result`), `run()` immediately throws an explicit `Error('Edge function did not return a response')` rather than defaulting to an empty response. > Sources: [packages/next/src/server/web/sandbox/sandbox.ts:158-158](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L158-L158) ### Full Worked Example: Sandbox Execution Runner The following example demonstrates how `run` initializes runtime context, handles request body stream cloning for non-GET/HEAD methods, and delegates execution through nested async storage contexts. ```typescript import { run } from './sandbox' import type { NodejsRequestData, FetchEventResult } from '../types' async function executeMiddlewareSandbox( requestData: NodejsRequestData, modulePaths: string[] ): Promise { const result = await run({ name: 'middleware', paths: modulePaths, request: requestData, useCache: true, edgeFunctionEntry: { assets: [], wasm: [], env: {}, }, distDir: '.next', clientAssetToken: 'secure-token-123', onError: (err) => console.error('Sandbox error:', err), onWarning: (warn) => console.warn('Sandbox warning:', warn), }) return result } ``` Sources: [packages/next/src/server/web/sandbox/sandbox.ts:29-43](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L29-L43), [packages/next/src/server/web/sandbox/sandbox.ts:111-163](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L111-L163) ## Development Bundling and Source Maps ### Overview The development bundler and hot reloader coordinate on-demand compilation, file watching, and source frame resolution for middleware and application routes. File watchers monitor project directories with an aggregate timeout of 5 milliseconds to detect modifications to configuration, environment files, or route entrypoints. When changes occur, the bundler aggregates file states, validates convention files, and maps source locations for error overlays. Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:402-422](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L402-L422) ### File Watching and Aggregation Call-Chain File change detection and entrypoint evaluation execute through a structured asynchronous pipeline when Watchpack triggers an aggregation event: 1. `wp.on('aggregated')` — Fires when file modifications settle past the 5ms aggregate timeout. 2. `wp.getTimeInfoEntries()` — Retrieves timestamps and metadata for all known files in the project workspace. 3. `absolutePathToPage()` — Normalizes scanned file paths into internal route identifiers based on page extensions and directory structure. 4. `isMiddlewareFile()` — Evaluates whether a modified file matches the middleware convention (`middleware.ts` or `proxy.ts`). 5. `getStaticInfoIncludingLayouts()` — Inspects static configuration and extracts middleware matchers from the file payload. Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:422-458](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L422-L458), [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:548-567](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L548-L567) > [!WARNING] > If both `middleware.ts` and `proxy.ts` are detected at the root convention level, the dev bundler immediately throws an error requiring the use of `proxy.ts` exclusively. > Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:479-486](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L479-L486) ### Source Map and Stack Frame Resolution Source maps and original stack frames are resolved through middleware handlers that query client, server, and edge server webpack compilations. The resolution sequence inspects native node source maps or webpack bundle modules to map compiled line and column positions back to original source files. | Middleware Endpoint | Method | Purpose | | --- | --- | --- | | `/__nextjs_original-stack-frames` | POST | Receives serialized stack frames and resolves them to original source positions and code frames | | `/__nextjs_source-map` | GET | Looks up compilation artifacts for a specified filename and returns its raw source map JSON payload | | `/__nextjs_launch-editor` | GET | Opens source files in the user's configured text editor at a specific line and column coordinate | Sources: [packages/next/src/server/dev/middleware-webpack.ts:592-677](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L592-L677), [packages/next/src/server/dev/middleware-webpack.ts:697-744](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L697-L744) > [!TIP] > When resolving original stack frames, sources containing `node_modules`, `next/dist`, or starting with `node:` are automatically marked as ignored unless explicitly requested otherwise. > Sources: [packages/next/src/server/dev/middleware-webpack.ts:36-43](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L36-L43), [packages/next/src/server/dev/middleware-webpack.ts:237-243](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L237-L243) ### Development Bundling Design Trade-Offs | Design Choice | Benefit | Cost | | --- | --- | --- | | Short 5ms Watchpack `aggregateTimeout` | Minimizes bootup dead time and speeds up initial reaction to file changes | Increases frequency of aggregation passes during rapid batch file modifications | | Multi-compilation stats fallback order (Client → Server → Edge) | Correctly targets the right bundle source map whether an error originates from client components, SSR, or edge runtime | Requires querying multiple webpack compilation graphs sequentially until a matching module ID or source map is found | | Eager ignore-list checking via `shouldIgnoreSource` | Keeps error overlays clean by automatically filtering out third-party framework internals and library frames | Adds path-string checking overhead during stack frame transformation | Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:402-403](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L402-L403), [packages/next/src/server/dev/middleware-webpack.ts:36-43](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L36-L43), [packages/next/src/server/dev/middleware-webpack.ts:485-515](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L485-L515) ## Related - [[Edge Sandbox Context]] - [[Routing and Normalization]] --- ## Technical docs: Dev Server and HMR URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/development-and-diagnostics/dev-server-and-hmr
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/dev/hot-reloader-webpack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts) - [packages/next/src/server/dev/hot-reloader-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts) - [packages/next/src/server/dev/next-dev-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts) - [packages/next/src/server/dev/on-demand-entry-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts) - [packages/next/src/cli/next-dev.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts) - [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx) - [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts) - [packages/next/src/server/dev/hot-middleware.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-middleware.ts) - [packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts) - [packages/next/src/server/lib/dev-bundler-service.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dev-bundler-service.ts) - [packages/next/src/server/dev/hot-reloader-rspack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-rspack.ts) - [packages/next/src/bundles/webpack/packages/lazy-compilation-web.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/lazy-compilation-web.js) - [packages/next/src/client/dev/noop-turbopack-hmr.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/noop-turbopack-hmr.ts) - [packages/next/src/client/next-dev-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/next-dev-turbopack.ts) - [packages/next/src/server/lib/find-page-file.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/find-page-file.ts) - [packages/next/src/shared/lib/utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/utils.ts)
## Overview The Next.js development server and Hot Module Replacement (HMR) subsystem bridges local source files with browser-side execution, handling compilation orchestration, dynamic route resolution, and real-time code updates. When developers run `next dev`, the CLI spins up an isolated worker process via Node.js IPC (`child_process.fork`), initializing the core `DevServer` class and binding an underlying bundler service powered by either Webpack (`HotReloaderWebpack`), Rspack (`HotReloaderRspack`), or Turbopack (`createHotReloaderTurbopack`). Sources: [packages/next/src/cli/next-dev.ts:394-427](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L394-L427) Rather than building an entire project monolithically on startup, Next.js utilizes on-demand entry compilation and file system watching (`Watchpack`) to track entries incrementally. The subsystem coordinates multi-compiler pipelines (client, server, and edge server targets) while managing a WebSocket or EventSource communication loop. This ensures that compiler stats, syntax errors, module graph changes, and Fast Refresh payloads stream instantly to the browser runtime without requiring full page reloads. Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:234-276](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L234-L276) ```mermaid flowchart TD CLI["next dev CLI (next-dev.ts)"] -->|forks child process| Server["DevServer / StartServer"] Server --> Setup["setupDevBundler()"] Setup -->|Choice: Turbo| Turbo["HotReloader (Turbopack)"] Setup -->|Choice: Webpack| Webpack["HotReloaderWebpack"] Setup -->|Choice: Rspack| Rspack["HotReloaderRspack"] Webpack --> OnDemand["onDemandEntryHandler()"] Rspack --> OnDemand OnDemand --> Watch["Watchpack File Watcher"] Webpack --> Middleware["WebpackHotMiddleware / WebSocket"] Middleware --> Browser["Browser HMR Runtime (React/Pages/App)"] ``` Sources: [packages/next/src/cli/next-dev.ts:394-427](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L394-L427), [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:234-276](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L234-L276) --- ## Dev Server Initialization and CLI Orchestration The development lifecycle begins in the Next.js CLI runner (`packages/next/src/cli/next-dev.ts`), which parses command flags—such as `--turbopack`, `--webpack`, `--port`, and `--inspect`—and prepares process environment variables before spawning the background worker server. Sources: [packages/next/src/cli/next-dev.ts:45-63](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L45-L63) When invoking `startServer`, the parent CLI forks a worker process with explicit Node.js options, custom memory allocations, and telemetry variables. The child communicates readiness through IPC messages (`nextWorkerReady`, `nextServerReady`), allowing the CLI manager to cleanly handle restarts (`RESTART_EXIT_CODE`), capture CPU profiles, and persist project metadata to `dev-state.json`. Sources: [packages/next/src/cli/next-dev.ts:429-448](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L429-L448) ```typescript child = fork(startServerPath, { stdio: 'inherit', execArgv, env: { ...defaultEnv, ...(isTurbopack ? { TURBOPACK: process.env.TURBOPACK } : undefined), __NEXT_DEV_SERVER: '1', NEXT_PRIVATE_WORKER: '1', NEXT_PRIVATE_TRACE_ID: traceId, NODE_OPTIONS: formattedNodeOptions, }, }) ``` Sources: [packages/next/src/cli/next-dev.ts:394-427](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L394-L427) --- ## Bundler Service Abstraction and Router Integration The `DevServer` class extends the production `Server` class, incorporating a `bundlerService` (`DevBundlerService`) interface that unifies operations between Webpack, Rspack, and Turbopack bundlers. Sources: [packages/next/src/server/dev/next-dev-server.ts:123-143](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts#L123-L143), [packages/next/src/server/lib/dev-bundler-service.ts:17-43](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dev-bundler-service.ts#L17-L43) `DevServer` overrides page component resolution methods (`findPageComponents`, `ensurePage`, `getCompilationError`) to delegate compilation requests directly to the active bundler service. If a page encounters compilation errors, `getCompilationError` inspects bundler diagnostics and wraps them in a `WrappedBuildError` to prevent duplicate console logging during request handling. Sources: [packages/next/src/server/dev/next-dev-server.ts:976-1034](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts#L976-L1034) | Method / Property | Return Type | Purpose in DevServer | | :--- | :--- | :--- | | `ensurePage(opts)` | `Promise` | Triggers compilation of a page entry on-demand if not already built. | | `getCompilationError(page)` | `Promise` | Retrieves compilation errors for a given page route from the bundler. | | `appIsrManifest` | `Record` | Exposes the Incremental Static Regeneration manifest state for cached routes. | | `sendHmrMessage(msg)` | `void` | Broadcasts an HMR message to all active browser client sockets. | Sources: [packages/next/src/server/dev/next-dev-server.ts:976-984](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts#L976-L984), [packages/next/src/server/dev/next-dev-server.ts:1043-1045](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts#L1043-L1045), [packages/next/src/server/lib/dev-bundler-service.ts:103-111](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dev-bundler-service.ts#L103-L111), [packages/next/src/server/lib/dev-bundler-service.ts:132-135](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dev-bundler-service.ts#L132-L135) --- ## On-Demand Entry Compilation To optimize resource utilization, Next.js does not compile all pages upfront. Instead, the `onDemandEntryHandler` (`packages/next/src/server/dev/on-demand-entry-handler.ts`) tracks active route entries dynamically as requests arrive. Sources: [packages/next/src/server/dev/on-demand-entry-handler.ts:541-570](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L541-L570) Entries are identified in the multi-compiler graph via structured keys generated by `getEntryKey`: `compilerType@pageBundleType@pageKey` Sources: [packages/next/src/server/dev/on-demand-entry-handler.ts:116-125](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L116-L125) For example, a client-side Pages router request for `/about` yields `client@pages@/about`, while an App router server file yields `server@app@app/page`. Sources: [packages/next/src/server/dev/on-demand-entry-handler.ts:116-125](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L116-L125) ```mermaid flowchart LR Request["Incoming Page Request"] --> Ensure["ensurePage()"] Ensure --> Check{"Entry exists & Built?"} Check -->|No| Add["Register Entry (ADDED)"] Add --> Compile["Compiler hooks.make (BUILDING)"] Compile --> Finish["Compiler hooks.done (BUILT)"] Check -->|Yes| Serve["Serve Compiled Output"] ``` Sources: [packages/next/src/server/dev/on-demand-entry-handler.ts:581-613](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L581-L613), [packages/next/src/server/dev/on-demand-entry-handler.ts:705-718](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L705-L718) The on-demand handler manages entry lifecycles through three core states: `ADDED`, `BUILDING`, and `BUILT`. Inactive entries past `maxInactiveAge` are automatically disposed of to free memory. Sources: [packages/next/src/server/dev/on-demand-entry-handler.ts:171-199](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L171-L199) ```typescript export const ADDED = Symbol('added') export const BUILDING = Symbol('building') export const BUILT = Symbol('built') ``` Sources: [packages/next/src/server/dev/on-demand-entry-handler.ts:171-174](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L171-L174) --- ## Hot Module Replacement (HMR) and Webpack Hot Middleware The Webpack HMR implementation (`WebpackHotMiddleware`) hooks into multi-compiler compilation events (`invalid`, `done`) across client, server, and edge-server compilers to broadcast build states to connected browser clients over WebSockets. Sources: [packages/next/src/server/dev/hot-middleware.ts:73-104](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-middleware.ts#L73-L104) ```mermaid sequenceDiagram participant Browser as Browser Client participant MW as WebpackHotMiddleware participant Compilers as Webpack MultiCompiler Compilers->>MW: compiler.hooks.done (Stats) MW->>MW: statsToJson() & getStatsForSyncEvent() MW-->>Browser: publish({ type: BUILT, hash, errors, warnings }) Browser->>Browser: tryApplyUpdatesWebpack() or Fast Refresh ``` Sources: [packages/next/src/server/dev/hot-middleware.ts:113-117](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-middleware.ts#L113-L117), [packages/next/src/server/dev/hot-middleware.ts:214-229](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-middleware.ts#L214-L229) When a compilation finishes, `WebpackHotMiddleware` computes whether server or client stats take precedence. If server compilation errors occur, server stats override client stats to ensure the error overlay displays backend/middleware compilation issues immediately. Sources: [packages/next/src/server/dev/hot-middleware.ts:55-71](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-middleware.ts#L55-L71) > [!NOTE] > `WebpackHotMiddleware` prioritizes server compiler stats when `serverStats.stats.hasErrors()` is true, preventing situations where the client compilation succeeds independently while a server-side route or middleware fails silently. Sources: [packages/next/src/server/dev/hot-middleware.ts:62-67](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-middleware.ts#L62-L67) --- ## Turbopack HMR Integration When Turbopack is enabled (`--turbopack`), Next.js replaces Webpack-specific hot reloading with Turbopack's native HMR client and server coordination (`packages/next/src/server/dev/hot-reloader-turbopack.ts`). Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:234-247](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L234-L247) The turbopack hot reloader maintains client subscription maps (`clientsWithoutHtmlRequestId`, `clientsByHtmlRequestId`) and enqueues compilation updates via `sendEnqueuedMessages`. If any active entry issue map contains non-warning errors, HMR event dispatches are delayed until compilation errors are fully resolved. Sources: [packages/next/src/server/dev/hot-reloader-turbopack.ts:697-700](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts#L697-L700), [packages/next/src/server/dev/hot-reloader-turbopack.ts:711-720](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts#L711-L720) ```typescript function sendEnqueuedMessages() { for (const [, issueMap] of currentEntryIssues) { if ( [...issueMap.values()].filter((i) => i.severity !== 'warning').length > 0 ) { // During compilation errors we want to delay the HMR events until errors are fixed return } } // Broadcast enqueued messages to connected clients... } ``` Sources: [packages/next/src/server/dev/hot-reloader-turbopack.ts:711-721](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts#L711-L721) --- ## Rspack Persistent Cache Management Rspack builds introduce persistent module graph caching that differs from Webpack's incremental design. Because the dev server starts with zero initial page entries, restoring from Rspack's persistent cache can purge module graphs if not managed. Sources: [packages/next/src/server/dev/hot-reloader-rspack.ts:11-28](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-rspack.ts#L11-L28) `HotReloaderRspack` (`packages/next/src/server/dev/hot-reloader-rspack.ts`) solves this by tracking successfully built page entries in `built-entries.json` under `.next/cache/rspack/`. After compilation completes, `afterCompile` verifies whether page files and entry paths still exist and validates their content hashes using SHA-256 before restoring them into the compiler state. Sources: [packages/next/src/server/dev/hot-reloader-rspack.ts:29-77](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-rspack.ts#L29-L77) ```typescript async function calculateFileHash( filePath: string, algorithm: string = 'sha256' ): Promise { if ( !(await fs.access(filePath).then( () => true, () => false )) ) { return } const fileBuffer = await fs.readFile(filePath) const hash = createHash(algorithm) hash.update(fileBuffer) return hash.digest('hex') } ``` Sources: [packages/next/src/server/dev/hot-reloader-rspack.ts:227-243](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-rspack.ts#L227-L243) --- ## Client-Side HMR Runtimes (Pages and App Router) Browser-side HMR behavior is split between Pages Router (`packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts`) and App Router (`packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx`). Sources: [packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts:95-125](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts#L95-L125), [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:123-145](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx#L123-L145) Both clients register message listeners via WebSocket connections to handle incoming synchronization payloads (`HMR_MESSAGE_SENT_TO_BROWSER.SYNC`), build successes (`handleSuccess`), and compilation errors (`handleErrors`). Sources: [packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts:98-104](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts#L98-L104), [packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts:142-147](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts#L142-L147), [packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts:210-215](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts#L210-L215) ```typescript // Attempt to update code on the fly, fall back to a hard reload. function tryApplyUpdatesWebpack(sendMessage: (message: string) => void) { if (!isUpdateAvailable() || !canApplyUpdates()) { resolvePendingHotUpdateWebpack() dispatcher.onBuildOk() reportHmrLatency(sendMessage, [], webpackStartMsSinceEpoch!, Date.now()) return } // Applies module hot updates via module.hot.check() } ``` Sources: [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:148-154](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx#L148-L154) If webpack hot module replacement fails or runtime errors are present (`RuntimeErrorHandler.hadRuntimeError`), the client triggers `performFullReload`, passing stack trace details and dependency chains to prevent inconsistent application state. Sources: [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:123-145](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx#L123-L145), [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:160-167](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx#L160-L167) ## Related - [[Dev Error Overlay]] - [[Bundler Integration]] --- ## Technical docs: Dev Error Overlay URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/development-and-diagnostics/dev-error-overlay
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/patch-error-inspect.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts) - [packages/next/src/server/dev/middleware-webpack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts) - [packages/next/src/server/dev/middleware-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-turbopack.ts) - [packages/next/src/next-devtools/userspace/pages/pages-dev-overlay-setup.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/pages/pages-dev-overlay-setup.tsx) - [packages/next/src/server/dev/browser-logs/source-map.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/browser-logs/source-map.ts) - [packages/next/src/next-devtools/dev-overlay.browser.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx) - [packages/next/src/server/lib/source-maps.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/source-maps.ts) - [packages/next/src/server/lib/install-code-frame.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/install-code-frame.ts) - [packages/next/src/next-devtools/userspace/app/errors/stitched-error.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/stitched-error.ts) - [packages/next/src/next-devtools/dev-overlay/components/code-frame/code-frame.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/code-frame/code-frame.tsx) - [packages/next/src/next-devtools/dev-overlay/container/errors.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/container/errors.tsx) - [packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts) - [packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx) - [packages/next/src/client/react-client-callbacks/error-boundary-callbacks.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/react-client-callbacks/error-boundary-callbacks.ts) - [packages/next/src/next-devtools/shared/stack-frame.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/shared/stack-frame.ts) - [packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx) - [packages/next/src/next-devtools/userspace/app/forward-logs.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/forward-logs.ts) - [packages/next/src/next-devtools/userspace/pages/hydration-error-state.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/pages/hydration-error-state.ts) - [packages/next/src/next-devtools/userspace/app/app-dev-overlay-setup.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/app-dev-overlay-setup.ts) - [packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts) - [packages/next/src/next-devtools/server/shared.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/server/shared.ts) - [packages/next/src/next-devtools/dev-overlay/components/call-stack-frame/call-stack-frame.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/call-stack-frame/call-stack-frame.tsx) - [packages/next/src/next-devtools/dev-overlay/container/runtime-error/index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/container/runtime-error/index.tsx) - [packages/next/src/next-devtools/dev-overlay/components/code-frame/parse-code-frame.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/code-frame/parse-code-frame.ts) - [packages/next/src/next-devtools/userspace/app/errors/intercept-console-error.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/intercept-console-error.ts) - [packages/next/src/next-devtools/userspace/app/client-entry.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/client-entry.tsx) - [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-call-stack/error-overlay-call-stack.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-call-stack/error-overlay-call-stack.tsx) - [packages/next/src/server/dev/node-stack-frames.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/node-stack-frames.ts) - [packages/next/src/next-devtools/dev-overlay/utils/generate-error-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/utils/generate-error-info.ts) - [packages/next/src/server/node-environment-extensions/error-inspect.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/server/node-environment-extensions/error-inspect.tsx)
## Overview The Dev Error Overlay is Next.js's browser-based diagnostic interface designed to capture, transform, and render runtime errors, unhandled rejections, console errors, and hydration mismatches during development. When an application throws an uncaught error in either the App or Pages router, the overlay intercepts the failure, sanitizes and decorates the error instance, fetches sourcemapped original code locations from the dev server, and presents an interactive stack trace accompanied by a highlighted code frame. By bridging client-side runtime boundaries with server-side bundler statistics (Webpack and Turbopack), the overlay resolves obfuscated production-style chunks back to original developer source files. It implements specialized handling for React hydration mismatches, error cause chains (`error.cause`), aggregate errors (`AggregateError`), and frame ignore-lists (`node_modules` or anonymous wrappers) to ensure developers focus exclusively on first-party application logic. Sources: [packages/next/src/next-devtools/dev-overlay.browser.tsx:189-200](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx#L189-L200) ```mermaid flowchart TD Error[Uncaught Error / Rejection / Console.error] --> Capture[Userspace Event Listeners] Capture --> Dispatch[Dispatcher & Queue Manager] Dispatch --> OverlayRoot[DevOverlayRoot & Reducer State] OverlayRoot --> FetchFrames[POST /__nextjs_original-stack-frames] FetchFrames --> Bundler{Bundler Context} Bundler -->|Webpack| WebpackMap[middleware-webpack.ts] Bundler -->|Turbopack| TurboMap[middleware-turbopack.ts] WebpackMap --> Render[CodeFrame & ErrorOverlayCallStack] TurboMap --> Render ``` Sources: [packages/next/src/server/dev/browser-logs/source-map.ts:32-77](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/browser-logs/source-map.ts#L32-L77) --- ## Client-Side Error Interception and Dispatching The Dev Error Overlay initializes event listeners in the browser environment to intercept global errors, unhandled promise rejections, and intercepted console errors. In the App Router, this setup is bootstrapped via `handleGlobalErrors()` and `patchConsoleError()`, while the Pages router mounts `PagesDevOverlayBridge`. Sources: [packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts:121-131](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts#L121-L131), [packages/next/src/next-devtools/userspace/pages/pages-dev-overlay-setup.tsx:112-126](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/pages/pages-dev-overlay-setup.tsx#L112-L126) When an unhandled error or rejection occurs, the event handler wraps or coerces the thrown value into a standard `Error` instance, attaches React owner stacks if available via `setOwnerStackIfAvailable()`, and enqueues microtasks to pass the error to the active overlay state handlers without blocking synchronous component rendering. Sources: [packages/next/src/next-devtools/userspace/app/errors/stitched-error.ts:13-28](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/stitched-error.ts#L13-L28), [packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts:22-57](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts#L22-L57) > [!NOTE] > Events dispatched during module evaluation or early lifecycle phases before React establishes its dispatch function are buffered into a local queue (`queue`) and replayed via `replayQueuedEvents()` once `maybeDispatch` is bound in `useInsertionEffect`. Sources: [packages/next/src/next-devtools/dev-overlay.browser.tsx:128-142](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx#L128-L142), [packages/next/src/next-devtools/dev-overlay.browser.tsx:242-251](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx#L242-L251) ```typescript // Example usage: Registering and dispatching an unhandled client error import { handleClientError } from '../next-devtools/userspace/app/errors/use-error-handler' try { // Application code that throws a runtime exception throw new Error('Failed to execute client operation') } catch (err) { handleClientError(err as Error) } ``` Sources: [packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts:48-57](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/errors/use-error-handler.ts#L48-L57) --- ## Stack Trace Parsing and Server-Side Resolution Raw stack traces captured in the browser contain obfuscated bundle paths (e.g., `_next/static/chunks/...`). To map these back to original author-time source files, the Dev Error Overlay transmits raw `StackFrame` structures to the development server via a `POST` request to `/__nextjs_original-stack-frames`. Sources: [packages/next/src/next-devtools/shared/stack-frame.ts:82-103](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/shared/stack-frame.ts#L82-L103) The server-side overlay middleware (`middleware-webpack.ts` or `middleware-turbopack.ts`) inspects the target compilation stats or Turbopack trace engine, resolves line and column positions against applicable source map payloads, and computes original stack frames and code frames. Sources: [packages/next/src/server/dev/middleware-webpack.ts:607-630](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L607-L630), [packages/next/src/server/dev/middleware-turbopack.ts:364-374](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-turbopack.ts#L364-L374) ```mermaid sequenceDiagram participant Client as Browser Overlay participant Server as Dev Server Middleware participant Bundler as Webpack / Turbopack Stats Client->>Server: POST /__nextjs_original-stack-frames Server->>Bundler: Query compilation chunks & source maps Bundler-->>Server: Return original position & code content Server-->>Client: Return OriginalStackFrameResponse[] ``` Sources: [packages/next/src/next-devtools/shared/stack-frame.ts:97-112](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/shared/stack-frame.ts#L97-L112), [packages/next/src/server/dev/middleware-webpack.ts:611-630](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L611-L630) --- ## Bundler Integration: Webpack vs. Turbopack The overlay abstracts bundler differences by routing mapping requests through `mapFramesUsingBundler()`. Sources: [packages/next/src/server/dev/browser-logs/source-map.ts:32-35](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/browser-logs/source-map.ts#L32-L35) - **Webpack:** Iterates through compilation targets in priority order (Client compilation first for Pages; Client, Server, then Edge Server compilations for App Router depending on rendering context) using `clientStats()`, `serverStats()`, and `edgeServerStats()`. Sources: [packages/next/src/server/dev/middleware-webpack.ts:481-516](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L481-L516) - **Turbopack:** Utilizes native source mapping via `nativeTraceSource()` or falls back to `batchedTraceSource()` to query Turbopack's project state directly. Sources: [packages/next/src/server/dev/middleware-turbopack.ts:303-307](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-turbopack.ts#L303-L307) | Bundler Strategy | Target Compilations Checked | Source Mapping Mechanism | | :--- | :--- | :--- | | **Webpack** | Client, Server, Edge Server | `getSource`, `getOriginalStackFrame`, `SourceMapConsumer` | | **Turbopack** | Project graph & native bindings | `nativeTraceSource`, `batchedTraceSource`, `SourceMapConsumer` | Sources: [packages/next/src/server/dev/middleware-webpack.ts:477-519](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L477-L519), [packages/next/src/server/dev/middleware-turbopack.ts:181-284](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-turbopack.ts#L181-L284) > [!WARNING] > If a source map is invalid or malformed, `nativeTraceSource` and `filterStackFrameDEV` catch the parsing error and log a warning without re-entering error inspection loops, preventing infinite error recursion. Sources: [packages/next/src/server/dev/middleware-turbopack.ts:186-193](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-turbopack.ts#L186-L193), [packages/next/src/server/lib/source-maps.ts:144-154](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/source-maps.ts#L144-L154) --- ## Code Frame Parsing, Formatting, and Rendering Once original stack frames are resolved, the overlay extracts and renders code snippets via the `CodeFrame` component. The raw code frame string is formatted by `formatCodeFrame()`, which strips excess indentation, and tokenized using `Anser` via `groupCodeFrameLines()` to support ANSI styling and class-based theming. Sources: [packages/next/src/next-devtools/dev-overlay/components/code-frame/code-frame.tsx:18-28](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/code-frame/code-frame.tsx#L18-L28), [packages/next/src/next-devtools/dev-overlay/components/code-frame/parse-code-frame.ts:6-41](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/code-frame/parse-code-frame.ts#L6-L41) `parseLineNumberFromCodeFrameLine()` parses individual line entries to identify line numbers and highlight errored lines (`data-nextjs-codeframe-line--errored="true"`). Sources: [packages/next/src/next-devtools/dev-overlay/components/code-frame/parse-code-frame.ts:82-98](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/code-frame/parse-code-frame.ts#L82-L98) ```typescript // Example usage: Parsing and rendering code frames directly import { formatCodeFrame, groupCodeFrameLines } from '../next-devtools/dev-overlay/components/code-frame/parse-code-frame' const rawCodeFrame = ` 1 | function MyComponent() { > 2 | throw new Error('Boom') | ^ 3 | } ` const formatted = formatCodeFrame(rawCodeFrame) const groupedLines = groupCodeFrameLines(formatted) ``` Sources: [packages/next/src/next-devtools/dev-overlay/components/code-frame/parse-code-frame.ts:6-73](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/code-frame/parse-code-frame.ts#L6-L73) --- ## Hydration Error State Integration Hydration mismatches between server-rendered HTML and client-side React trees require specialized diagnostics. The overlay captures hydration warnings via `storeHydrationErrorStateFromConsoleArgs()` in `hydration-error-state.ts`, distinguishing between React 18 and React 19 warning signatures. Sources: [packages/next/src/next-devtools/userspace/pages/hydration-error-state.ts:58-106](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/pages/hydration-error-state.ts#L58-L106) For React 18, `generateHydrationDiffReact18()` parses component stack traces from console arguments to build an ASCII tree diff highlighting unexpected server vs. client tag structures or text nodes. Sources: [packages/next/src/next-devtools/userspace/pages/hydration-error-state.ts:126-186](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/pages/hydration-error-state.ts#L126-L186) ```typescript // Example usage: Attaching hydration error state to a runtime error import { attachHydrationErrorState, getSquashedHydrationErrorDetails } from '../next-devtools/userspace/pages/hydration-error-state' const error = new Error('Hydration failed because the initial UI does not match what was rendered on the server.') attachHydrationErrorState(error) const hydrationDetails = getSquashedHydrationErrorDetails(error) ``` Sources: [packages/next/src/next-devtools/userspace/pages/hydration-error-state.ts:19-54](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/pages/hydration-error-state.ts#L19-L54) --- ## Error Aggregation, Causes, and Call Stacks When errors chain multiple nested exceptions or aggregate multiple failures, `getErrorByType()` constructs a structured error tree. Sources: [packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts:49-89](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts#L49-L89) - **Cause Chains:** `getCauseChain()` recursively inspects `error.cause` up to a maximum depth of `5`. Sources: [packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts:91-127](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts#L91-L127) - **Aggregate Errors:** `getAggregateErrors()` unpacks `AggregateError` instances, processing up to `5` child errors (`maxErrors = 5`). Sources: [packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts:129-180](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts#L129-L180) - **Call Stack Tallying:** `ErrorOverlayCallStack` computes `ignoredFramesTally` and provides an interactive toggle (`onToggleIgnoreList`) that dynamically adjusts container dialog heights using `transitionend` event listeners. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-call-stack/error-overlay-call-stack.tsx:17-57](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-call-stack/error-overlay-call-stack.tsx#L17-L57) > [!IMPORTANT] > Both `getCauseChain` and `getAggregateErrors` enforce a maximum recursion depth of `5` to prevent stack overflow vulnerabilities and infinite processing loops when handling circular or deeply nested error causes. Sources: [packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts:91-96](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts#L91-L96), [packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts:129-135](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/utils/get-error-by-type.ts#L129-L135) ## Related - [[DevTools Panel]] - [[Dev Server and HMR]] --- ## Technical docs: DevTools Panel URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/development-and-diagnostics/devtools-panel
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx) - [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-navs-panel.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-navs-panel.tsx) - [packages/next/src/next-devtools/dev-overlay.browser.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx) - [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/nodejs-inspector-button.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/nodejs-inspector-button.tsx) - [packages/next/src/server/dev/hot-reloader-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts) - [packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx) - [packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx) - [packages/next/src/next-devtools/dev-overlay/shared.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/shared.ts) - [packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx) - [packages/next/src/next-devtools/entrypoint.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/entrypoint.ts) - [packages/next/src/next-devtools/dev-overlay/menu/panel-router.css](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/panel-router.css) - [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/next-logo.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/next-logo.tsx) - [packages/next/src/next-devtools/dev-overlay/components/copy-button/index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/copy-button/index.tsx) - [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/copy-error-button.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/copy-error-button.tsx) - [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx) - [packages/next/src/client/dev/debug-channel.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/debug-channel.ts) - [packages/next/src/next-devtools/dev-overlay/menu/context.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/context.tsx) - [packages/next/src/next-devtools/userspace/pages/pages-dev-overlay-setup.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/pages/pages-dev-overlay-setup.tsx) - [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx) - [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx) - [packages/next/src/server/mcp/tools/get-page-metadata.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts) - [packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/route-info.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/route-info.tsx) - [packages/next/src/next-devtools/dev-overlay.shim.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.shim.ts) - [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx) - [packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx) - [packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/cache-disabled.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/cache-disabled.tsx) - [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts) - [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-nav/error-overlay-nav.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-nav/error-overlay-nav.tsx) - [packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx) - [packages/next/src/next-devtools/dev-overlay-ux.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay-ux.ts)
## Overview The DevTools Panel provides an interactive, in-browser development overlay and command center designed to inspect, diagnose, and configure Next.js applications during local development. It surfaces critical runtime information, route structures, compilation metrics, and instant navigation behaviors directly inside userspace. Sources: [packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx#L43-L108) By isolating its user interface within a shadow DOM root and coordinating through context providers, the DevTools Panel empowers developers to inspect route segment trees, trigger boundary fallbacks, analyze navigation diagnostics, and manage user preferences without leaving the browser environment. Sources: [packages/next/src/next-devtools/dev-overlay.browser.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx#L253-L331) ## DevTools Architecture and Shadow Portal ### Overview The DevTools architecture relies on a specialized entrypoint structure, a shadow DOM isolation mechanism, and router error boundary wrappers to integrate the development overlay into Next.js applications without contaminating userspace styles or runtime scopes. The entrypoint module exports core browser overlay controllers while maintaining shim fallbacks for unsupported environments. Sources: [packages/next/src/next-devtools/entrypoint.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/entrypoint.ts#L1-L2), [packages/next/src/next-devtools/dev-overlay.shim.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.shim.ts#L1-L26) ### Bootstrap Lifecycle and Queuable Dispatcher Events occurring during module evaluation or before React establishes a dispatch connection are intercepted by a queueing mechanism. The `createQueuable` wrapper stores incoming dispatcher actions until the root reducer connects via `maybeDispatch`. Sources: [packages/next/src/next-devtools/dev-overlay.browser.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx#L128-L142) ```typescript function createQueuable( queueableFunction: (dispatch: Dispatch, ...args: Args) => void ) { return (...args: Args) => { if (maybeDispatch) { queueableFunction(maybeDispatch, ...args) } else { queue.push((dispatch: Dispatch) => { queueableFunction(dispatch, ...args) }) } } } ``` Sources: [packages/next/src/next-devtools/dev-overlay.browser.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx#L130-L142) The initialization lifecycle executes through insertion and layout effects inside `DevOverlayRoot`, coordinating theme classes and event replays: `DevOverlayRoot` mount → `useInsertionEffect` assigns `maybeDispatch = dispatch` → `setTimeout` schedules `replayQueuedEvents(dispatch)` → `queue` items execute sequentially → `useLayoutEffect` synchronizes theme classes (`dark` or `light`) onto the shadow root host element. Sources: [packages/next/src/next-devtools/dev-overlay.browser.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx#L279-L307) > [!NOTE] > Fonts must be loaded outside the shadow DOM root because standard stylesheet encapsulation prevents font face rule inheritance across shadow boundaries; `FontStyles` renders directly into the outer document tree. Sources: [packages/next/src/next-devtools/dev-overlay.browser.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay.browser.tsx#L317-L318) ### ShadowPortal Isolation and Styles The DevOverlay wraps its UI components inside a `ShadowPortal` and loads specialized component styles and scale updaters to guarantee visual isolation from the host application. Sources: [packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx#L43-L57), [packages/next/src/next-devtools/dev-overlay-ux.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay-ux.ts#L1-L6) | Asset / Component | Source File | Purpose | | :--- | :--- | :--- | | `FontStyles` | [packages/next/src/next-devtools/dev-overlay/font/font-styles.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/font/font-styles.tsx) | Injects required typography definitions outside the shadow boundary. Sources: [packages/next/src/next-devtools/dev-overlay-ux.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay-ux.ts#L4-L4) | | `ComponentStyles` | [packages/next/src/next-devtools/dev-overlay/styles/component-styles.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/styles/component-styles.tsx) | Encapsulates widget styles within the shadow root. Sources: [packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx#L3-L3) | | `ScaleUpdater` | [packages/next/src/next-devtools/dev-overlay/styles/scale-updater.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/styles/scale-updater.tsx) | Manages scaling metrics for overlay responsiveness. Sources: [packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx#L6-L6) | | `DevOverlay` | [packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx#L43-L108) | Main orchestration container for errors, panels, and indicators. Sources: [packages/next/src/next-devtools/dev-overlay-ux.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay-ux.ts#L5-L5) | ### Router Error Boundary Wrappers Application and Pages routers integrate error boundaries to capture runtime exceptions and interface with the dev tools dispatcher. In the App Router, `AppDevOverlayErrorBoundary` catches render errors, flags runtime error status, and opens the error overlay via `dispatcher.openErrorOverlay()`. Sources: [packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx#L42-L72) ```typescript export class AppDevOverlayErrorBoundary extends PureComponent< AppDevOverlayErrorBoundaryProps, AppDevOverlayErrorBoundaryState > { static contextType = AppRouterContext declare context: AppRouterInstance | null state: AppDevOverlayErrorBoundaryState = { error: null, } static getDerivedStateFromError( thrownValue: Error ): Partial { RuntimeErrorHandler.hadRuntimeError = true return { error: { thrownValue }, } } componentDidCatch(err: unknown) { if ( process.env.NODE_ENV === 'development' && isError(err) && err.message === SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE ) { return } dispatcher.openErrorOverlay() } } ``` Sources: [packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx#L42-L72) > [!WARNING] > Simulated segment explorer errors (`SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE`) are intentionally ignored by `componentDidCatch` to prevent false error triggers during tree inspection. Sources: [packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx#L63-L70) ## DevTools Indicator and Status Display ### Overview The DevTools indicator serves as the primary floating entrypoint for the Next.js development overlay. Managed by `DevToolsIndicator`, it wraps a draggable region and the Next.js logo badge, positioning itself dynamically within the viewport according to configured offsets and panel states. Sources: [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx#L21-L73) ### Status Indicator and Status Enumeration The `StatusIndicator` component manages active compilation and rendering states. The underlying `Status` enum defines operational lifecycle states that determine the color and content of the indicator badge, giving visual priority to compilation tasks over rendering processes. Sources: [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx#L4-L34) | Status Enum Value | String Representation | Status Dot Color | Purpose | | :--- | :--- | :--- | :--- | | `Status.None` | `''` | (none) | Indicator is hidden when idle. Sources: [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx#L4-L6) | | `Status.Compiling` | `'Compiling'` | `#f5a623` (orange) | Indicates an active build or compilation pass. Sources: [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx#L9-L9), [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx#L54-L54) | | `Status.Rendering` | `'Rendering'` | `#50e3c2` (teal) | Indicates a standard client-side transition or render pass. Sources: [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx#L6-L6), [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx#L55-L55) | | `Status.RenderingColdCache` | `'Rendering (cold cache)'` | `#f5a623` (orange) | Render pass hit an unpopulated cache. Sources: [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx#L7-L7), [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx#L56-L56) | | `Status.RenderingCacheDisabled` | `'Rendering (cache disabled)'` | `#f5a623` (orange) | Render pass occurred while caches were bypassed. Sources: [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx#L8-L8), [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx#L57-L57) | The function `getCurrentStatus()` resolves the current status hierarchy from building flags, rendering flags, and cache states: ```typescript export function getCurrentStatus( buildingIndicator: boolean, renderingIndicator: boolean, cacheIndicator: CacheIndicatorState ): Status { if (buildingIndicator) { return Status.Compiling } if (renderingIndicator) { if (cacheIndicator === 'cold') { return Status.RenderingColdCache } if (cacheIndicator === 'bypass') { return Status.RenderingCacheDisabled } return Status.Rendering } return Status.None } ``` Sources: [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx#L12-L34) > [!NOTE] > Compilation checks take precedence over rendering checks inside `getCurrentStatus`. While a client transition is pending, cache states color the rendering status before settling into a persistent cache badge. Sources: [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/status-indicator.tsx#L17-L23) ### Drag Positioning and Panel Synchronization The indicator container uses `Draggable` to let developers reposition the widget. When a drag action updates the indicator position, `DevToolsIndicator` dispatches position actions and invokes `useUpdateAllPanelPositions` to synchronize open panel placements across the workspace. Sources: [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx#L44-L57), [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx#L81-L112) ```typescript export const useUpdateAllPanelPositions = () => { const { state, dispatch } = useDevOverlayContext() return (position: DevToolsIndicatorPosition) => { dispatch({ type: ACTION_DEVTOOLS_PANEL_POSITION, devToolsPanelPosition: position, key: STORE_KEY_SHARED_PANEL_LOCATION, }) const panelPositionKeys = Object.keys(state.devToolsPanelPosition).filter( (key) => key.startsWith(STORAGE_KEY_PANEL_POSITION_PREFIX) ) const panelPositionPatch: Record = { [STORE_KEY_SHARED_PANEL_LOCATION]: position, } panelPositionKeys.forEach((key) => { dispatch({ type: ACTION_DEVTOOLS_PANEL_POSITION, devToolsPanelPosition: position, key, }) panelPositionPatch[key] = position }) saveDevToolsConfig({ devToolsPanelPosition: panelPositionPatch, }) } } ``` Sources: [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx#L81-L111) > [!CAUTION] > Dragging is disabled (`disableDrag={panel !== null}`) whenever any panel is actively open. This prevents desynchronization bugs and UI jank between the floating logo and its expanding menu panels. Sources: [packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx#L44-L46) ### User Preferences and Server Controls The `UserPreferencesBody` component provides configuration controls within the DevTools info interface. It allows users to modify themes, adjust indicator positions and scale sizes, configure hide shortcuts, restart the development server, or clear bundler caches. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx#L17-L35) | Preference Option | Handler Function | Action / Storage Effect | | :--- | :--- | :--- | | **Theme** | `handleThemeChange` | Toggles portal host classes (`dark`, `light`, or clears both for system) and calls `saveDevToolsConfig({ theme })`. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx#L39-L57) | | **Position** | `handlePositionChange` | Updates position state via `setPosition()` and persists with `saveDevToolsConfig({ devToolsPosition })`. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx#L59-L64) | | **Size / Scale** | `handleSizeChange` | Parses numeric scale value, updates scale state, and saves via `saveDevToolsConfig({ scale })`. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx#L66-L70) | | **Restart Dev Server** | Inline button handler | Invokes `restartServer({ invalidateFileSystemCache: false })`. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx#L188-L199) | | **Reset Bundler Cache** | Inline button handler | Invokes `restartServer({ invalidateFileSystemCache: true })` if `process.env.__NEXT_BUNDLER_HAS_PERSISTENT_CACHE` is enabled. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/user-preferences.tsx#L202-L225) | ## Panel Routing and Navigation Context ### Overview The panel routing and navigation context coordinates sub-views within the development overlay via the `PanelRouterContext` and `MenuPanel` components. It maps active states, route inspection menus, and cache status panels to specific view identifiers. Sources: [packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx#L1-L38), [packages/next/src/next-devtools/dev-overlay/menu/context.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/context.tsx#L8-L24) ### Panel State Kinds and Menu Definitions The `PanelStateKind` type defines all available sub-panel views that can be rendered through the router context. The `MenuPanel` component populates the dev overlay menu items based on current runtime flags, issue counts, bundler settings, and caching indicators. Sources: [packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx#L37-L189), [packages/next/src/next-devtools/dev-overlay/menu/context.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/context.tsx#L8-L17) | PanelStateKind Value | Menu Trigger Label / Condition | Associated Body / Component | | :--- | :--- | :--- | | `preferences` | Preferences (GearIcon, footer item) | `UserPreferencesBody`. Sources: [packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx#L177-L185) | | `route-type` | Route (Static / Dynamic indicator) | `RouteInfoBody`. Sources: [packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx#L93-L110) | | `segment-explorer` | Route Info (ChevronRight, App Router only) | `SegmentExplorer` / `PageSegmentTree`. Sources: [packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx#L169-L176) | | `instant-navs` | Navigation Inspector (ChevronRight, `__NEXT_INSTANT_NAV_TOGGLE`) | `InstantNavsPanel`. Sources: [packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx#L137-L148) | | `cache-disabled` | Cache (Disabled, `cacheIndicator === 'bypass'`) | `CacheDisabledBody`. Sources: [packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx#L149-L158) | | `cold-cache` | Cache (Cold, `cacheIndicator === 'cold'`) | `ColdCacheBody`. Sources: [packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx#L159-L168) | | `turbo-info` | Bundler (Turbopack status / upgrade link) | External link / Turbopack info. Sources: [packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx#L111-L131) | | `panel-selector` | Internal panel switcher context | Dynamic panel containers. Sources: [packages/next/src/next-devtools/dev-overlay/menu/context.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/context.tsx#L8-L17) | ### Route and Cache Status Panels When a route is evaluated, `RouteInfoBody` switches between `StaticRouteContent` and `DynamicRouteContent` based on the `isStaticRoute` boolean flag and `routerType` (`'pages'` or `'app'`). For static routes, it displays prerendering information; for dynamic routes, it explains request-time rendering and points to dynamic APIs or `fetch({ cache: 'no-store' })` triggers. The `CacheDisabledBody` component warns developers when all caches were bypassed due to browser devtools configuration, hard reloads, or draft mode. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/route-info.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/route-info.tsx#L3-L130), [packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/cache-disabled.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/dev-tools-info/cache-disabled.tsx#L3-L21) > [!NOTE] > The error overlay toggle click handler checks `state.isErrorOverlayOpen`: if true, it dispatches `ACTION_ERROR_OVERLAY_CLOSE` and resets the panel to `null`; otherwise, it sets the panel to `null`, resets `selectedIndex` to `-1`, and dispatches `ACTION_ERROR_OVERLAY_OPEN`. Sources: [packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/menu/panel-router.tsx#L82-L91) ## Route Segment Explorer and Metadata ### Overview The Route Segment Explorer visualizes the active App Router segment hierarchy and boundary state in the Next.js DevTools overlay. It maintains interactive segment trees, boundary override counters, and triggers simulation states for runtime error, loading, and not-found boundaries. Sources: [packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx#L25-L61), [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx#L87-L98) ### Segment Tree Visualization and State Management The component renders the segment structure via `PageSegmentTree`, which queries `useSegmentTree()` and computes the active boundary override count using `countActiveBoundaries()`. Global resets invoke `traverseTreeAndResetBoundaries()`, resetting boundary types across all trie nodes. File pills (`FilePill`) render icons depending on whether a file is a builtin segment or custom user code, and clicking any file label triggers `openInEditor()`. Sources: [packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx#L25-L151), [packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx#L450-L462) > [!WARNING] > `SegmentTrieNode` registers and unregisters node state using `useLayoutEffect` with `dispatcher.segmentExplorerNodeAdd(nodeState)` and `dispatcher.segmentExplorerNodeRemove(nodeState)`. Standard `useEffect` will fail to preserve state updates correctly during suspense boundaries. Sources: [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx#L50-L57) ### Boundary Triggers and Simulation Types Userspace segment nodes interact with `SegmentStateProvider` and `SegmentBoundaryTriggerNode` to simulate boundary fallbacks. When `boundaryType` is activated on a node, `SegmentBoundaryTriggerNode` mounts the corresponding fallback component (`LoadingSegmentNode`, `NotFoundSegmentNode`, or `ErrorSegmentNode`). Sources: [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx#L62-L98), [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx#L129-L161) | SegmentBoundaryType | Trigger Action / Implementation | Behavior | | :--- | :--- | :--- | | `loading` | `use(forever)` | Suspends the component render via a pending Promise. Sources: [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx#L70-L74) | | `not-found` | `notFound()` | Triggers Next.js `not-found` boundary render. Sources: [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx#L62-L64) | | `error` | `throw new Error(SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE)` | Throws simulated error to activate error boundary. Sources: [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx#L66-L68) | | `global-error` | Validation check in `PageSegmentTreeLayerPresentation` | Detects missing global error boundaries. Sources: [packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/overview/segment-explorer.tsx#L180-L200) | ### MCP Metadata Inspection and Tool Integration The Model Context Protocol (MCP) server registers the `get_page_metadata` tool via `registerGetPageMetadataTool()`. This tool verifies active browser connections, sends `HMR_MESSAGE_SENT_TO_BROWSER.REQUEST_PAGE_METADATA` requests, and processes responses through `convertSegmentTrieToPageMetadata()`. Sources: [packages/next/src/server/mcp/tools/get-page-metadata.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts#L19-L79) ```typescript export function registerGetPageMetadataTool( server: McpServer, sendHmrMessage: (message: HmrMessageSentToBrowser) => void, getActiveConnectionCount: () => number ) { server.registerTool( 'get_page_metadata', { description: 'Get runtime metadata about what contributes to the current page render from active browser sessions.', inputSchema: {}, }, async (_request) => { mcpTelemetryTracker.recordToolCall('mcp/get_page_metadata') const connectionCount = getActiveConnectionCount() if (connectionCount === 0) { return { content: [{ type: 'text', text: JSON.stringify({ error: 'No browser sessions connected...' }) }] } } const responses = await createBrowserRequest( HMR_MESSAGE_SENT_TO_BROWSER.REQUEST_PAGE_METADATA, sendHmrMessage, getActiveConnectionCount, DEFAULT_BROWSER_REQUEST_TIMEOUT_MS ) return { content: [{ type: 'text', text: JSON.stringify(formatPageMetadata(responses)) }] } } ) } ``` Sources: [packages/next/src/server/mcp/tools/get-page-metadata.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts#L19-L117) > [!TIP] > When `formatPageMetadata` processes segment trees for MCP output, it sorts segments by `typeOrder` (`layout` → boundary → `page` → other) and normalizes paths by stripping `@boundary` and `__next_builtin__` prefixes before serializing session results. Sources: [packages/next/src/server/mcp/tools/get-page-metadata.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts#L194-L236) ## Instant Navigation Diagnostics and Guidance ### Overview The instant navigation panel coordinates testing and diagnostics for prerendered and prefetched UI states in Next.js applications. It monitors state via `COOKIE_NAME` (`next-instant-navigation-testing`), tracks transitions with `useSyncExternalStore`, and drives AI prompt generation and fix recommendation cards. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-navs-panel.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-navs-panel.tsx#L207-L214), [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts#L23-L27), [packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx#L61-L105) ### Instant Navigation Panel States and Cookie Tracking The instant navigation cookie serves as the sole source of truth for tracking capture status, storing JSON arrays that represent pending states, captured MPA page loads, and captured SPA navigation route trees. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-navs-panel.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-navs-panel.tsx#L211-L214), [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts#L4-L10) | Cookie Array Structure | State Representation | Meaning / Description | | :--- | :--- | :--- | | `[0, id]` | `pending` | Waiting to capture instant navigation events. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts#L4-L5) | | `[1, id, null]` | `mpa` | Captured MPA page load displaying prerendered UI. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts#L6-L7) | | `[1, id, { from, to }]` | `spa` | Captured SPA navigation from/to route trees. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts#L8-L9) | > [!NOTE] > The raw cookie string acts as the `useSyncExternalStore` snapshot, relying on value comparisons for referential stability while parsing structured tree data via `useMemo` during renders. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts#L83-L86), [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-nav-cookie.ts#L142-L146) ### Transition Timing Layers and Call Execution When a user triggers "Continue Rendering", the capture session performs a multi-step state machine execution: `clearInstantNavCaptureCookie()` deletes the cookie, `state.renderingIndicator` transitions through pending phases, and `cookieStore.set()` writes a new pending cookie value to re-arm capture. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-navs-panel.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-navs-panel.tsx#L198-L262) ```typescript // Re-arm call chain execution walkthrough clearInstantNavCaptureCookie() → setInstantNavTransientStatus('idle') → cookieStore.delete(COOKIE_NAME) → state.renderingIndicator (false → true) → setInstantNavTransientStatus('rearming-awaiting-end') → state.renderingIndicator (true → false) → setInstantNavTransientStatus('rearming-awaiting-cookie') → cookieStore.set(...) ``` Sources: [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-navs-panel.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-navs-panel.tsx#L198-L261) > [!WARNING] > Unmounting the panel resets transient UI states via `ACTION_INSTANT_NAVS_RESET`, but Fast Refresh remounts preserve active captures by avoiding cookie deletion unless the router panel explicitly changes away from `'instant-navs'`. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-navs-panel.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant-navs/instant-navs-panel.tsx#L215-L232) ### Fix Recommendation Cards and Guidance Integration The `InstantGuidance` component constructs diagnostic fix recommendation cards using `getCards(kind, variant, cause)`. It supports copyable AI prompts via `CopyPromptButton`, combining rule titles, step instructions, and failure code blocks. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx#L61-L105), [packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx#L208-L223) | Guidance Kind | Variant Types | Documentation Target URL Pattern | | :--- | :--- | :--- | | `sync-io` | `runtime`, `dynamic` | `SYNC_IO_DOCS[cause]` or `DOCS_URLS['sync-io']`. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx#L225-L226) | | `sync-io-client` | `runtime`, `dynamic` | `SYNC_IO_CLIENT_DOCS[cause]` or `DOCS_URLS['sync-io-client']`. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx#L227-L228) | | `blocking-route` | `runtime`, `dynamic` | `https://nextjs.org/docs/messages/blocking-prerender-{runtime\|dynamic}`. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx#L229-L233) | | `metadata` | `runtime`, `dynamic` | `https://nextjs.org/docs/messages/blocking-prerender-metadata-{runtime\|dynamic}`. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx#L234-L238) | | `viewport` | `runtime`, `dynamic` | `https://nextjs.org/docs/messages/blocking-prerender-viewport-{runtime\|dynamic}`. Sources: [packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/instant/instant-guidance.tsx#L239-L243) | ## Error Overlay Toolbar and Utilities ### Overview The `ErrorOverlayToolbar` and associated utility components provide controls within the error overlay navigation header, allowing developers to copy error details and stack traces, launch or attach the Node.js debugger, navigate to documentation, and inspect version staleness. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx#L17-L43), [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-nav/error-overlay-nav.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-nav/error-overlay-nav.tsx#L26-L70) ### Toolbar Component Architecture and Layout The `ErrorOverlayToolbar` component renders inside `ErrorOverlayNav` as a flex container with a gap of 6px (reducing to 4px on viewports under 575px wide). It orchestrates four primary toolbar utilities: `CopyErrorButton`, `DocsLinkButton`, `NodejsInspectorButton`, and `VersionStalenessInfo`. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx#L17-L43), [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-nav/error-overlay-nav.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-nav/error-overlay-nav.tsx#L60-L67) | Toolbar Utility / Element | Props / Configuration | Purpose / Action | | :--- | :--- | :--- | | `CopyErrorButton` | `error`, `generateErrorInfo` | Copies generated runtime error stack and metadata to clipboard. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx#L29-L29) | | `DocsLinkButton` | `errorMessage` | Opens relevant Next.js documentation based on the error message. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx#L30-L30) | | `NodejsInspectorButton` | `defaultDevtoolsFrontendUrl` | Attaches the Node.js inspector or copies the Chrome DevTools frontend URL. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx#L31-L34) | | `VersionStalenessInfo` | `versionInfo`, `bundlerName` | Displays version staleness information for the specified bundler. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/error-overlay-toolbar.tsx#L35-L39) | ### Copy Button Utilities and State Machine The underlying `CopyButton` component uses React's `useActionState` hook to manage clipboards via a state machine with three copy states: `initial`, `success`, and `error`. It relies on an asynchronous `getContent()` function provider or direct `content` string props. Sources: [packages/next/src/next-devtools/dev-overlay/components/copy-button/index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/copy-button/index.tsx#L4-L46), [packages/next/src/next-devtools/dev-overlay/components/copy-button/index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/copy-button/index.tsx#L71-L96) ```typescript // CopyButton execution walkthrough copy() → React.startTransition() → dispatch('copy') → getContentString() → navigator.clipboard.writeText(content) → { state: 'success' } (or { state: 'error', error }) ``` Sources: [packages/next/src/next-devtools/dev-overlay/components/copy-button/index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/copy-button/index.tsx#L23-L40), [packages/next/src/next-devtools/dev-overlay/components/copy-button/index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/copy-button/index.tsx#L48-L52) > [!NOTE] > When `copyState.state` transitions to `'success'`, a 2000ms timeout automatically dispatches a `'reset'` action to revert the button back to the `'initial'` label and icon state. Sources: [packages/next/src/next-devtools/dev-overlay/components/copy-button/index.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/copy-button/index.tsx#L105-L115) ### Node.js Inspector Launcher Integration The `NodejsInspectorButton` manages debugging connections by posting requests to the development server endpoint `/__nextjs_attach-nodejs-inspector`. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/nodejs-inspector-button.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/nodejs-inspector-button.tsx#L247-L250) | Action State Status | Condition | Rendered Output / Behavior | | :--- | :--- | :--- | | `fulfilled` (with URL) | `devtoolsFrontendUrl` is defined | Renders `CopyButton` with Node.js icon to copy Chrome DevTools frontend URL. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/nodejs-inspector-button.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/nodejs-inspector-button.tsx#L275-L323) | | `fulfilled` (without URL) | `devtoolsFrontendUrl` is undefined | Renders inspector button with disabled icon; clicking fires `attachDebugger`. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/nodejs-inspector-button.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/nodejs-inspector-button.tsx#L288-L307) | | `rejected` | `fetch` or JSON parsing failed | Logs error via `console.error` and displays retry tooltip. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/nodejs-inspector-button.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/nodejs-inspector-button.tsx#L280-L284) | > [!WARNING] > If the backend inspector attachment endpoint returns a non-ok response, the action catches the error and rejects with a custom message prefixed by `Failed to attach Node.js inspector:`. Sources: [packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/nodejs-inspector-button.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/components/errors/error-overlay-toolbar/nodejs-inspector-button.tsx#L251-L270) ## Related - [[Dev Error Overlay]] --- ## Technical docs: MCP Tool Integration URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/development-and-diagnostics/mcp-tool-integration
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/dev/hot-reloader-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts) - [packages/next/src/server/mcp/get-or-create-mcp-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts) - [packages/next/src/server/dev/hot-reloader-webpack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts) - [packages/next/src/server/mcp/tools/get-compilation-issues.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-compilation-issues.ts) - [packages/next/src/server/mcp/tools/compile-route.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/compile-route.ts) - [packages/next/src/server/dev/on-demand-entry-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts) - [packages/next/src/server/mcp/tools/get-errors.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-errors.ts) - [packages/next/src/server/mcp/tools/get-logs.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-logs.ts) - [packages/next/src/cli/internal/turbo-trace-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts) - [packages/next/src/server/mcp/get-mcp-middleware.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-mcp-middleware.ts) - [packages/next/src/server/mcp/tools/get-project-metadata.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-project-metadata.ts) - [packages/next/src/server/mcp/tools/get-page-metadata.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts) - [packages/next/src/server/mcp/mcp-telemetry-tracker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/mcp-telemetry-tracker.ts) - [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts) - [packages/next/src/server/mcp/tools/next-instance-error-state.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/next-instance-error-state.ts) - [packages/next/src/server/mcp/tools/get-routes.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-routes.ts) - [packages/next/src/cli/internal/query-trace.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/query-trace.ts) - [packages/next/src/server/dev/next-dev-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts) - [packages/next/src/shared/lib/mcp-error-types.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/mcp-error-types.ts) - [packages/next/src/server/mcp/tools/get-server-action-by-id.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-server-action-by-id.ts) - [packages/next/src/server/mcp/tools/utils/browser-communication.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/utils/browser-communication.ts) - [packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts) - [packages/next/src/shared/lib/mcp-page-metadata-types.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/mcp-page-metadata-types.ts) - [packages/next/src/next-devtools/server/restart-dev-server-middleware.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/server/restart-dev-server-middleware.ts) - [packages/next/src/server/lib/router-utils/instrumentation-globals.external.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/instrumentation-globals.external.ts) - [packages/next/src/server/dev/middleware-webpack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts) - [packages/next/src/server/dev/middleware-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-turbopack.ts) - [packages/next/src/next-devtools/server/devtools-config-middleware.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/server/devtools-config-middleware.ts) - [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx) - [packages/next/src/server/patch-error-inspect.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts)
## Overview The Next.js Model Context Protocol (MCP) tool integration provides a standardized interface for AI agents and external clients to interact directly with a running Next.js development server and build pipeline. By exposing structured tools over HTTP and streamable transport layers, the MCP server allows external agents to inspect project and page metadata, enumerate routes, trigger on-demand route compilation, query compilation diagnostics, and retrieve error states or development logs without requiring manual browser navigation. Sources: [packages/next/src/server/mcp/get-or-create-mcp-server.ts:1-76](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L1-L76) Sources: [packages/next/src/server/mcp/get-mcp-middleware.ts:1-44](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-mcp-middleware.ts#L1-L44) Sources: [packages/next/src/server/mcp/tools/compile-route.ts:1-103](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/compile-route.ts#L1-L103) ## Server Architecture and HTTP Middleware ### Overview The Next.js Model Context Protocol (MCP) server instance is managed via a lazy initialization lifecycle linked directly to the development server's hot reloaders. When experimental MCP server support is enabled via configuration (`experimental.mcpServer`), the dev server registers HTTP middleware inside both Turbopack and Webpack hot reloaders. This middleware intercepts incoming requests matching the `/_next/mcp` prefix, connects an isolated streamable transport, and delegates execution to the core MCP server instance. Sources: [packages/next/src/server/dev/hot-reloader-turbopack.ts:1034-1046](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts#L1034-L1046) Sources: [packages/next/src/server/mcp/get-or-create-mcp-server.ts:33-76](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L33-L76) Sources: [packages/next/src/server/mcp/get-mcp-middleware.ts:9-44](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-mcp-middleware.ts#L9-L44) ### Lifecycle and Middleware Integration The lifecycle of the MCP server begins with `getOrCreateMcpServer`, which guards against duplicate initialization by retaining a module-level `mcpServer` singleton reference. Upon first invocation, it instantiates an `McpServer` with the name `'Next.js MCP Server'` and version `'0.2.0'`, registering the foundational tools such as `get-project-metadata`, `get-errors`, `get-page-metadata`, `get-logs`, `get-server-action-by-id`, and `get-routes`. Turbopack-specific capabilities like `get-compilation-issues` and `compile-route` are conditionally attached when their corresponding options are supplied. Sources: [packages/next/src/server/mcp/get-or-create-mcp-server.ts:1-76](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L1-L76) HTTP requests entering the dev server flow through `getMcpMiddleware`, which performs path filtering, connection cleanup, and request body parsing before handing the payload off to the MCP SDK transport layer. ```mermaid sequenceDiagram participant Client as HTTP Client participant MW as getMcpMiddleware participant Factory as getOrCreateMcpServer participant Transport as StreamableHTTPServerTransport participant Server as McpServer Client->>MW: HTTP Request (/_next/mcp) MW->>MW: Verify pathname starts with /_next/mcp MW->>Factory: getOrCreateMcpServer(options) Factory-->>MW: McpServer singleton MW->>Transport: new StreamableHTTPServerTransport() MW->>Server: mcpServer.connect(transport) MW->>MW: parseBody(req, 1MB limit) MW->>Transport: transport.handleRequest(req, res, parsedBody) Transport-->>Client: JSON-RPC Response Note over MW,Transport: On connection close, transport.close() is invoked ``` Sources: [packages/next/src/server/mcp/get-mcp-middleware.ts:9-44](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-mcp-middleware.ts#L9-L44) ### Middleware Execution Walkthrough When an incoming HTTP request hits the Next.js development server, the middleware pipeline executes a precise sequence of checks and operations to handle JSON-RPC messaging. 1. `getMcpMiddleware` receives `req` (IncomingMessage), `res` (ServerResponse), and `next` (`() => void`). Sources: [packages/next/src/server/mcp/get-mcp-middleware.ts:9-14](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-mcp-middleware.ts#L9-L14) 2. It parses the request URL and evaluates whether `pathname` starts with `/_next/mcp`. If it does not match, control immediately falls through by invoking `next()`. Sources: [packages/next/src/server/mcp/get-mcp-middleware.ts:15-18](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-mcp-middleware.ts#L15-L18) 3. If the path matches, `getOrCreateMcpServer(options)` is called to retrieve or initialize the `McpServer` instance with its registered tools. Sources: [packages/next/src/server/mcp/get-or-create-mcp-server.ts:33-76](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L33-L76) Sources: [packages/next/src/server/mcp/get-mcp-middleware.ts:19-19](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-mcp-middleware.ts#L19-L19) 4. A new `StreamableHTTPServerTransport` is instantiated with `sessionIdGenerator: undefined`. Sources: [packages/next/src/server/mcp/get-mcp-middleware.ts:20-22](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-mcp-middleware.ts#L20-L22) 5. A `'close'` listener is bound to the response object (`res`) to guarantee that `transport.close()` is invoked if the connection drops prematurely. Sources: [packages/next/src/server/mcp/get-mcp-middleware.ts:23-26](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-mcp-middleware.ts#L23-L26) 6. `mcpServer.connect(transport)` establishes the session bridge, after which `parseBody(req, 1024 * 1024)` parses up to 1 megabyte of incoming JSON-RPC payload data. Sources: [packages/next/src/server/mcp/get-mcp-middleware.ts:27-28](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-mcp-middleware.ts#L27-L28) 7. `transport.handleRequest(req, res, parsedBody)` processes the execution request. If an exception occurs and headers have not yet been sent, a `500` status code with a JSON-RPC formatted error object (`code: -32000`, message: `'Internal server error'`) is written to the response. Sources: [packages/next/src/server/mcp/get-mcp-middleware.ts:29-42](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-mcp-middleware.ts#L29-L42) > [!NOTE] > The `compile_route` tool and compilation issue subscriptions are exclusively available when running under Turbopack (`getTurbopackProject`). When initializing the MCP middleware inside the Webpack hot reloader, `compile_route` is intentionally omitted from the server options. Sources: [packages/next/src/server/dev/hot-reloader-turbopack.ts:1034-1047](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts#L1034-L1047) Sources: [packages/next/src/server/dev/hot-reloader-webpack.ts:1681-1694](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts#L1681-L1694) ### Server Configuration Options | Option Name | Type | Purpose | Sources | | :--- | :--- | :--- | :--- | | `projectPath` | `string` | Root filesystem path of the Next.js development project. | [packages/next/src/server/mcp/get-or-create-mcp-server.ts:15-16](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L15-L16) | | `distDir` | `string` | Distribution output directory path (e.g., `.next`). | [packages/next/src/server/mcp/get-or-create-mcp-server.ts:15-17](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L15-L17) | | `nextConfig` | `NextConfigComplete` | Fully resolved Next.js configuration object. | [packages/next/src/server/mcp/get-or-create-mcp-server.ts:15-18](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L15-L18) | | `pagesDir` | `string \| undefined` | Absolute path to the legacy `pages` directory, if present. | [packages/next/src/server/mcp/get-or-create-mcp-server.ts:15-19](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L15-L19) | | `appDir` | `string \| undefined` | Absolute path to the App Router `app` directory, if present. | [packages/next/src/server/mcp/get-or-create-mcp-server.ts:15-20](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L15-L20) | | `sendHmrMessage` | `(message) => void` | Callback to broadcast HMR control messages to connected browser clients. | [packages/next/src/server/mcp/get-or-create-mcp-server.ts:15-21](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L15-L21) | | `getActiveConnectionCount` | `() => number` | Returns the total count of active browser HMR client connections. | [packages/next/src/server/mcp/get-or-create-mcp-server.ts:15-22](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L15-L22) | | `getDevServerUrl` | `() => string \| undefined` | Resolves the private origin URL of the running dev server instance. | [packages/next/src/server/mcp/get-or-create-mcp-server.ts:15-23](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L15-L23) | | `getTurbopackProject` | `() => Project \| undefined` | Accessor for the active Turbopack project reference. | [packages/next/src/server/mcp/get-or-create-mcp-server.ts:15-24](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L15-L24) | | `compileRoute` | `(opts) => Promise` | Turbopack-exclusive callback to trigger on-demand route compilation. | [packages/next/src/server/mcp/get-or-create-mcp-server.ts:15-29](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/get-or-create-mcp-server.ts#L15-L29) | ## Route Discovery and On-Demand Compilation ### Overview The Model Context Protocol (MCP) server provides tools to discover entry routes across `app/` and `pages/` directories and trigger targeted on-demand route compilation without executing live HTTP requests. These capabilities allow clients to inspect application structure, warm module graphs, measure build latencies, and perform memory benchmarking. Sources: [packages/next/src/server/mcp/tools/compile-route.ts:1-9](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/compile-route.ts#L1-L9) Sources: [packages/next/src/server/mcp/tools/get-routes.ts:1-14](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-routes.ts#L1-L14) ### Route Discovery via `get_routes` The `get_routes` tool scans the project filesystem directly to locate all route files in the App Router and Pages Router directories, returning them grouped by router type. Sources: [packages/next/src/server/mcp/tools/get-routes.ts:1-10](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-routes.ts#L1-L10) ```mermaid sequenceDiagram participant Client participant get_routes as registerGetRoutesTool participant Discovery as discoverRoutes Client->>get_routes: Call tool (optional routerType) get_routes->>get_routes: Validate pagesDir / appDir existence get_routes->>Discovery: Parallel scan (appDir, pagesDir) Discovery-->>get_routes: Return raw route definitions get_routes->>get_routes: Map, sort, and group results get_routes-->>Client: JSON response { appRouter, pagesRouter } ``` Sources: [packages/next/src/server/mcp/tools/get-routes.ts:30-146](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-routes.ts#L30-L146) When invoked, the tool records telemetry via `mcpTelemetryTracker.recordToolCall('mcp/get_routes')`, validates that at least one directory exists, and independently executes `discoverRoutes` for both App and Pages routers in parallel to ensure a failure in one router does not block the other. Sources: [packages/next/src/server/mcp/tools/get-routes.ts:41-91](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-routes.ts#L41-L91) > [!NOTE] > Dynamic route segments are returned as defined in the filesystem (e.g., `[id]`, `[slug]`, ` [...slug]`). The `get_routes` tool does not expand `generateStaticParams` or dynamic parameters. Sources: [packages/next/src/server/mcp/tools/get-routes.ts:11-13](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-routes.ts#L11-L13) ### On-Demand Compilation via `compile_route` The `compile_route` tool triggers on-demand compilation through the development server handler, simulating the code path executed when a user first visits a route. Sources: [packages/next/src/server/mcp/tools/compile-route.ts:1-9](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/compile-route.ts#L1-L9) | Parameter Name | Type | Description | Sources | | :--- | :--- | :--- | :--- | | `routeSpecifier` | `string` (optional) | Resolved route specifier from `get_routes` (e.g., `"/"`, `"/blog/[slug]"`). Mutually exclusive with `path`. | [packages/next/src/server/mcp/tools/compile-route.ts:31-38](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/compile-route.ts#L31-L38) | | `path` | `string` (optional) | URL path on the site (e.g., `"/blog/hello-world"`). Query strings are ignored. Mutually exclusive with `routeSpecifier`. | [packages/next/src/server/mcp/tools/compile-route.ts:39-47](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/compile-route.ts#L39-L47) | Execution follows a strict validation and error-handling flow: 1. `mcpTelemetryTracker.recordToolCall('mcp/compile_route')` logs the tool invocation. Sources: [packages/next/src/server/mcp/tools/compile-route.ts:50-51](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/compile-route.ts#L50-L51) 2. The input validator checks exclusivity: exactly one of `routeSpecifier` or `path` must be provided, returning an error response if both or neither are supplied. Sources: [packages/next/src/server/mcp/tools/compile-route.ts:53-65](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/compile-route.ts#L53-L65) 3. `compileRoute({ routeSpecifier, path })` is awaited. On success, it returns `{ routeSpecifier, issues }`. Sources: [packages/next/src/server/mcp/tools/compile-route.ts:67-80](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/compile-route.ts#L67-L80) 4. If an exception occurs, the catch block inspects the error code; an `ENOENT` error returns `{ notFound: true, input }`, while general compilation failures return the serialized error message. Sources: [packages/next/src/server/mcp/tools/compile-route.ts:81-99](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/compile-route.ts#L81-L99) > [!WARNING] > Providing both `routeSpecifier` and `path`, or omitting both parameters entirely, results in an immediate `isError: true` JSON response requiring exactly one argument. Sources: [packages/next/src/server/mcp/tools/compile-route.ts:53-65](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/compile-route.ts#L53-L65) ## Compilation Issues and Development Logs ### Overview The Model Context Protocol (MCP) server integrates tools for inspecting development diagnostics, specifically focusing on Turbopack compilation errors across all routes and locating the Next.js development log files. These capabilities allow an AI agent to proactively analyze codebases without requiring a live browser session. Sources: [packages/next/src/server/mcp/tools/get-compilation-issues.ts:1-9](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-compilation-issues.ts#L1-L9) Sources: [packages/next/src/server/mcp/tools/get-logs.ts:1-6](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-logs.ts#L1-L6) ### Compilation Issues Tool The `get_compilation_issues` tool builds the module graph for every application endpoint and collects diagnostics directly from Turbopack, covering module-not-found errors, syntax issues, and transform failures. Sources: [packages/next/src/server/mcp/tools/get-compilation-issues.ts:1-9](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-compilation-issues.ts#L1-L9) ```typescript export function registerGetCompilationIssuesTool( server: McpServer, getProject: () => Project | undefined ) { server.registerTool( 'get_compilation_issues', { description: 'Build the module graph for all routes and return all compilation issues (resolve errors, missing modules, transform errors, etc.). Does not require a browser session. Covers all routes proactively.', inputSchema: {}, }, async () => { mcpTelemetryTracker.recordToolCall('mcp/get_compilation_issues') try { const project = getProject() if (!project) { return { content: [ { type: 'text', text: JSON.stringify({ error: 'Turbopack project is not available. This tool requires the Turbopack bundler.', }), }, ], } } const { issues } = await project.getAllCompilationIssues() const formattedIssues = formatCompilationIssues(issues) return { content: [ { type: 'text', text: JSON.stringify({ issues: formattedIssues }), }, ], } } catch (error) { return { content: [ { type: 'text', text: JSON.stringify({ error: error instanceof Error ? error.message : String(error), }), }, ], } } } ) } ``` Sources: [packages/next/src/server/mcp/tools/get-compilation-issues.ts:15-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-compilation-issues.ts#L15-L70) > [!NOTE] > Unlike `get_errors`, which relies on an active browser session to reflect the runtime error overlay, `get_compilation_issues` evaluates all project routes proactively through Turbopack without opening a browser. Sources: [packages/next/src/server/mcp/tools/get-compilation-issues.ts:4-9](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-compilation-issues.ts#L4-L9) ### Development Log File Tool The `get_logs` tool exposes the filesystem path to the Next.js development log file, enabling agents to read browser console logs and development events directly. Sources: [packages/next/src/server/mcp/tools/get-logs.ts:1-6](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-logs.ts#L1-L6) ```typescript export function registerGetLogsTool(server: McpServer, distDir: string) { server.registerTool( 'get_logs', { description: 'Get the path to the Next.js development log file. Returns the file path so the agent can read the logs directly.', }, async () => { // Track telemetry mcpTelemetryTracker.recordToolCall('mcp/get_logs') try { const logFilePath = join(distDir, 'logs', 'next-development.log') // Check if the log file exists try { await stat(logFilePath) } catch (error) { return { content: [ { type: 'text', text: JSON.stringify({ error: `Log file not found at ${logFilePath}.`, }), }, ], } } return { content: [ { type: 'text', text: JSON.stringify({ logFilePath, }), }, ], } } catch (error) { return { content: [ { type: 'text', text: JSON.stringify({ error: `Error getting log file path: ${error instanceof Error ? error.message : String(error)}`, }), }, ], } } } ) } ``` Sources: [packages/next/src/server/mcp/tools/get-logs.ts:12-66](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-logs.ts#L12-L66) > [!WARNING] > If the development log file has not yet been initialized under `{nextConfig.distDir}/logs/next-development.log`, the tool catches the filesystem `stat` error and returns an explicit JSON error payload stating that the log file was not found. Sources: [packages/next/src/server/mcp/tools/get-logs.ts:26-40](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-logs.ts#L26-L40) ## Error State Collection and Formatting ### Overview The error reporting and collection subsystem combines Next.js instance-level configuration validation errors, build errors, and browser runtime errors into structured output via the Model Context Protocol (MCP). The `get_errors` tool orchestrates error retrieval by checking active browser connections, dispatching bidirectional HMR communication requests, combining route-specific overlay states with global instance errors, and invoking source-mapped stack trace inspection. Sources: [packages/next/src/server/mcp/tools/get-errors.ts:1-15](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-errors.ts#L1-L15) ```typescript export const NextInstanceErrorState: { nextConfig: unknown[] } = { nextConfig: [], } ``` Sources: [packages/next/src/server/mcp/tools/next-instance-error-state.ts:18-22](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/next-instance-error-state.ts#L18-L22) > [!NOTE] > `NextInstanceErrorState` captures global instance errors that are not associated with a specific browser session or route, such as validation errors within `next.config.js`. Sources: [packages/next/src/server/mcp/tools/next-instance-error-state.ts:1-7](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/next-instance-error-state.ts#L1-L7) ### Bidirectional Browser Overlay Error Synchronization Retrieving runtime browser errors requires communication between the MCP server and connected browser sessions using Hot Module Replacement (HMR) messaging. The communication utility manages pending requests with unique identifiers, connection counts, and timeout handlers. ```typescript export function createBrowserRequest( messageType: HMR_MESSAGE_SENT_TO_BROWSER, sendHmrMessage: (message: HmrMessageSentToBrowser) => void, getActiveConnectionCount: () => number, timeoutMs: number ): Promise[]> { const connectionCount = getActiveConnectionCount() if (connectionCount === 0) { return Promise.resolve([]) } const requestId = `mcp-${messageType}-${nanoid()}` const responsePromise = new Promise[]>( (resolve, reject) => { const timeout = setTimeout(() => { const pending = pendingRequests.get(requestId) if (pending && pending.responses.length > 0) { resolve(pending.responses as BrowserResponse[]) } else { reject( new Error( `Timeout waiting for response from frontend. The browser may not be responding to HMR messages.` ) ) } pendingRequests.delete(requestId) }, timeoutMs) pendingRequests.set(requestId, { responses: [], expectedCount: connectionCount, resolve: resolve as (value: BrowserResponse[]) => void, reject, timeout, }) } ) sendHmrMessage({ type: messageType, requestId, } as HmrMessageSentToBrowser) return responsePromise } ``` Sources: [packages/next/src/server/mcp/tools/utils/browser-communication.ts:30-75](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/utils/browser-communication.ts#L30-L75) The execution flow for gathering browser error state follows a precise sequence: 1. `get_errors` tool execution invokes `getActiveConnectionCount()` and records telemetry via `mcpTelemetryTracker.recordToolCall('mcp/get_errors')`. Sources: [packages/next/src/server/mcp/tools/get-errors.ts:44-61](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-errors.ts#L44-L61) 2. `createBrowserRequest()` generates a unique `requestId` prefixed with `mcp-`, registers a pending request entry mapped by ID, and initializes a timer for `DEFAULT_BROWSER_REQUEST_TIMEOUT_MS` (5000ms). Sources: [packages/next/src/server/mcp/tools/utils/browser-communication.ts:13-67](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/utils/browser-communication.ts#L13-L67) 3. `sendHmrMessage()` transmits the `HMR_MESSAGE_SENT_TO_BROWSER.REQUEST_CURRENT_ERROR_STATE` message containing the `requestId` to the browser frontend. Sources: [packages/next/src/server/mcp/tools/get-errors.ts:63-68](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-errors.ts#L63-L68) Sources: [packages/next/src/server/mcp/tools/utils/browser-communication.ts:69-74](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/utils/browser-communication.ts#L69-L74) 4. The browser responds via HMR, triggering `handleErrorStateResponse()` which invokes `handleBrowserPageResponse()`. Sources: [packages/next/src/server/mcp/tools/get-errors.ts:130-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-errors.ts#L130-L140) 5. `handleBrowserPageResponse()` pushes the received error state and URL into the pending request's responses array. Once `responses.length >= expectedCount`, it clears the timeout, resolves the promise, and purges the pending map entry. Sources: [packages/next/src/server/mcp/tools/utils/browser-communication.ts:77-97](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/utils/browser-communication.ts#L77-L97) > [!WARNING] > If zero browser connections are active when `get_errors` runs, the tool returns an immediate JSON message instructing the user to open the application in a browser, bypassing the HMR request step entirely. Sources: [packages/next/src/server/mcp/tools/get-errors.ts:48-61](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-errors.ts#L48-L61) ### Source-Mapped Stack Frame Inspection and Error Patching To make stack traces readable during debugging, Next.js patches error inspection routines for Node.js (`patchErrorInspectNodeJS`) and Edge Lite (`patchErrorInspectEdgeLite`) runtimes. Stack frames are parsed, mapped back to original source files via source map consumers, and cleaned up by ignoring framework or Node internal frames. ```typescript export function patchErrorInspectNodeJS( errorConstructor: ErrorConstructor ): void { const inspectSymbol = Symbol.for('nodejs.util.inspect.custom') errorConstructor.prepareStackTrace = prepareUnsourcemappedStackTrace // @ts-expect-error -- TODO upstream types errorConstructor.prototype[inspectSymbol] = function ( depth: number, inspectOptions: util.InspectOptions, inspect: typeof util.inspect ): string { // avoid false-positive dynamic i/o warnings e.g. due to usage of `Math.random` in `source-map`. return workUnitAsyncStorage.exit(() => { const newError = sourceMapError(this, inspectOptions) const originalCustomInspect = (newError as any)[inspectSymbol] // Prevent infinite recursion. // { customInspect: false } would result in `error.cause` not using our inspect. Object.defineProperty(newError, inspectSymbol, { value: undefined, enumerable: false, writable: true, }) try { return inspect(newError, { ...inspectOptions, depth, }) } finally { ;(newError as any)[inspectSymbol] = originalCustomInspect } }) } } ``` Sources: [packages/next/src/server/patch-error-inspect.ts:499-534](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts#L499-L534) | Error Inspect Function | Runtime Environment | Custom Inspect Symbol | Behavior on Error Customization | Sources | | :--- | :--- | :--- | :--- | :--- | | `patchErrorInspectNodeJS` | Node.js | `Symbol.for('nodejs.util.inspect.custom')` | Exits workUnitAsyncStorage, maps source stack, strips custom inspect symbol, and invokes util.inspect. | [packages/next/src/server/patch-error-inspect.ts:499-534](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts#L499-L534) | | `patchErrorInspectEdgeLite` | Edge Lite | `Symbol.for('edge-runtime.inspect.custom')` | Exits workUnitAsyncStorage, maps source stack, strips custom inspect symbol, and formats via edge formatter. | [packages/next/src/server/patch-error-inspect.ts:536-567](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts#L536-L567) | > [!CAUTION] > Both error patching functions execute inside `workUnitAsyncStorage.exit()` to prevent false-positive dynamic I/O warnings caused by internal dependencies such as random number generation in the `source-map` library. Sources: [packages/next/src/server/patch-error-inspect.ts:512-514](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts#L512-L514) Sources: [packages/next/src/server/patch-error-inspect.ts:549-551](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts#L549-L551) ## Page Metadata and Action Introspection ### Overview Next.js Model Context Protocol (MCP) tooling exposes runtime page segment trees, absolute project filepaths, developer server URLs, and Server Action mappings by communicating directly with active browser sessions and reading internal build manifests. This introspection layer enables AI assistants and debugging clients to examine the live structure of an application without manual DOM inspection or guesswork. Sources: [packages/next/src/server/mcp/tools/get-page-metadata.ts:24-30](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts#L24-L30) Sources: [packages/next/src/server/mcp/tools/get-project-metadata.ts:9-15](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-project-metadata.ts#L9-L15) Sources: [packages/next/src/server/mcp/tools/get-server-action-by-id.ts:22-31](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-server-action-by-id.ts#L22-L31) ### Runtime Page Metadata and Segment Traversal The `get_page_metadata` tool queries connected browser sessions for runtime page segment data via WebSocket communication. When invoked, it checks active client connections, issues an `HMR_MESSAGE_SENT_TO_BROWSER.REQUEST_PAGE_METADATA` payload, converts the returned trie into structured page segments, and formats the metadata grouped by session URL and router type. ```typescript export function registerGetPageMetadataTool( server: McpServer, sendHmrMessage: (message: HmrMessageSentToBrowser) => void, getActiveConnectionCount: () => number ) { server.registerTool( 'get_page_metadata', { description: 'Get runtime metadata about what contributes to the current page render from active browser sessions.', inputSchema: {}, }, async (_request) => { // Track telemetry mcpTelemetryTracker.recordToolCall('mcp/get_page_metadata') try { const connectionCount = getActiveConnectionCount() if (connectionCount === 0) { return { content: [ { type: 'text', text: JSON.stringify({ error: 'No browser sessions connected. Please open your application in a browser to retrieve page metadata.', }), }, ], } } const responses = await createBrowserRequest( HMR_MESSAGE_SENT_TO_BROWSER.REQUEST_PAGE_METADATA, sendHmrMessage, getActiveConnectionCount, DEFAULT_BROWSER_REQUEST_TIMEOUT_MS ) // ... conversion and formatting ``` Sources: [packages/next/src/server/mcp/tools/get-page-metadata.ts:19-56](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts#L19-L56) During trie conversion, `convertSegmentTrieToPageMetadata` traverses the segment tree recursively. Nodes containing a value push a `PageSegment` record containing the node's `type`, `pagePath`, and `boundaryType` into the output array before visiting child nodes. ```typescript function convertSegmentTrieToPageMetadata(data: SegmentTrieData): PageMetadata { const segments: PageSegment[] = [] if (data.segmentTrie) { // Traverse the trie and collect all segments function traverseTrie(node: SegmentTrieNode): void { if (node.value) { segments.push({ type: node.value.type, pagePath: node.value.pagePath, boundaryType: node.value.boundaryType, }) } for (const childNode of Object.values(node.children)) { if (childNode) { traverseTrie(childNode) } } } traverseTrie(data.segmentTrie) } return { segments, routerType: data.routerType, } } ``` Sources: [packages/next/src/server/mcp/tools/get-page-metadata.ts:132-160](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts#L132-L160) > [!WARNING] > If `getActiveConnectionCount()` returns `0`, `get_page_metadata` immediately returns an error JSON object without attempting browser communication. Users must open the application in a browser session to populate active connections. Sources: [packages/next/src/server/mcp/tools/get-page-metadata.ts:36-49](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts#L36-L49) ### Project Metadata and Server Action Resolution The `get_project_metadata` tool evaluates project path availability and dev server URLs, returning absolute paths and endpoints for MCP clients. Complementing this, `get_server_action_by_id` inspects compiled build output to locate Server Actions by their unique string identifier within `server-reference-manifest.json`. ```typescript export function registerGetActionByIdTool(server: McpServer, distDir: string) { server.registerTool( 'get_server_action_by_id', { description: 'Locates a Server Action by its ID in the server-reference-manifest.json. Returns the filename and export name for the action.', inputSchema: { actionId: z.string(), }, }, async (request) => { // Track telemetry mcpTelemetryTracker.recordToolCall('mcp/get_server_action_by_id') try { const { actionId } = request if (!actionId) { return { content: [ { type: 'text', text: JSON.stringify({ error: 'actionId parameter is required', }), }, ], } } const manifestPath = join( distDir, 'server', 'server-reference-manifest.json' ) ``` Sources: [packages/next/src/server/mcp/tools/get-server-action-by-id.ts:22-56](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-server-action-by-id.ts#L22-L56) When an action ID is supplied, the tool parses both `node` and `edge` records inside the server reference manifest. If an exported name starts with the inline action prefix `$$RSC_SERVER_ACTION_`, the returned function name is reported as `'inline server action'`; otherwise, the actual export name is preserved. ```typescript const manifest: ServerReferenceManifest = JSON.parse(manifestContent) // Search in node entries if (manifest.node && manifest.node[actionId]) { const entry = manifest.node[actionId] const isInlineAction = entry.exportedName.startsWith(INLINE_ACTION_PREFIX) return { content: [ { type: 'text', text: JSON.stringify( { actionId, runtime: 'node', filename: entry.filename, functionName: isInlineAction ? 'inline server action' : entry.exportedName, layer: entry.layer, workers: entry.workers, }, null, 2 ), }, ], } } ``` Sources: [packages/next/src/server/mcp/tools/get-server-action-by-id.ts:74-102](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-server-action-by-id.ts#L74-L102) | MCP Introspection Tool | Input Schema | Target Artifact / Source | Returned Properties / Details | Sources | | :--- | :--- | :--- | :--- | :--- | | `get_project_metadata` | `{}` | Runtime project state | `projectPath`, `devServerUrl`, or `error`. | [packages/next/src/server/mcp/tools/get-project-metadata.ts:11-45](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-project-metadata.ts#L11-L45) | | `get_page_metadata` | `{}` | Connected browser sessions via HMR | `sessions` array containing session `url`, `routerType`, and sorted `segments`. | [packages/next/src/server/mcp/tools/get-page-metadata.ts:26-103](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts#L26-L103) | | `get_server_action_by_id` | `{ actionId: string }` | `distDir/server/server-reference-manifest.json` | `actionId`, `runtime` (`node` or `edge`), `filename`, `functionName`, `layer`, and `workers`. | [packages/next/src/server/mcp/tools/get-server-action-by-id.ts:28-56](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-server-action-by-id.ts#L28-L56) | > [!TIP] > Segment sorting in `formatPageMetadata` prioritizes layout segments (`0`), boundary types (`1`), page segments (`2`), and fallback types (`3`), with tie-breaking handled alphabetically via `localeCompare` on `pagePath`. Sources: [packages/next/src/server/mcp/tools/get-page-metadata.ts:194-206](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts#L194-L206) ## Tool Telemetry and Trace Server ### Overview The MCP telemetry and trace server infrastructure records tool call invocation metrics and exposes native Turbopack profiling spans via an integrated Model Context Protocol server. The telemetry tracker manages an in-memory usage map associating each feature name with its execution count, which can be flushed and recorded through standard telemetry events. ```typescript class McpTelemetryTracker { private usageMap = new Map() recordToolCall(toolName: McpToolName): void { const current = this.usageMap.get(toolName) || 0 this.usageMap.set(toolName, current + 1) } getUsages(): McpToolUsage[] { return Array.from(this.usageMap.entries()).map(([featureName, count]) => ({ featureName, invocationCount: count, })) } reset(): void { this.usageMap.clear() } hasUsage(): boolean { return this.usageMap.size > 0 } } export const mcpTelemetryTracker = new McpTelemetryTracker() ``` Sources: [packages/next/src/server/mcp/mcp-telemetry-tracker.ts:13-50](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/mcp-telemetry-tracker.ts#L13-L50) > [!NOTE] > When `recordMcpTelemetry` is called with a telemetry instance, it retrieves active tool usages via `getMcpTelemetryUsage()`, loads the build telemetry events module dynamically, and iterates through generated events to record each invocation count. Sources: [packages/next/src/server/mcp/mcp-telemetry-tracker.ts:69-83](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/mcp-telemetry-tracker.ts#L69-L83) ### Trace Server CLI and Query Engine The Turbopack trace server CLI (`startTurboTraceServerCli`) loads native SWC bindings, starts a background trace server handle on a WebSocket port, and sets up an MCP server instance registered with the `query_spans` tool. ```typescript export async function startTurboTraceServerCli( file: string, port: number | undefined, mcpPort: number | undefined ) { const wsPort = port ?? DEFAULT_WS_PORT const httpPort = mcpPort ?? wsPort + 1 let bindings = await loadBindings() let handle = bindings.turbo.startTurbopackTraceServerHandle(file, wsPort) const mcpServer = new McpServer({ name: 'Next.js Trace Server MCP', version: '0.1.0', }) mcpServer.registerTool( 'query_spans', { description: 'Query spans from a turbopack trace file...', inputSchema: { parent: z.string().optional(), aggregated: z.boolean().optional(), sort: z.enum(['value', 'name']).optional(), search: z.string().optional(), page: z.number().optional(), outputType: z.enum(['markdown', 'json']).optional(), }, }, (args) => { const result = bindings.turbo.queryTraceSpans(handle, { parent: args.parent, aggregated: args.aggregated ?? true, sort: args.sort, search: args.search, page: args.page ?? 1, }) // Returns spans, page, totalPages, and totalCount } ) } ``` Sources: [packages/next/src/cli/internal/turbo-trace-server.ts:114-233](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L114-L233) Client interactions with the trace server are mediated by the `queryTraceCli` utility, which constructs a JSON-RPC request targeting the `/mcp` HTTP endpoint and parses Server-Sent Events (SSE) data streams to extract response text. ```typescript export async function queryTraceCli(options: QueryTraceOptions): Promise { const port = options.port ?? DEFAULT_MCP_PORT const args: Record = {} if (options.parent !== undefined) args.parent = options.parent if (options.aggregated !== undefined) args.aggregated = options.aggregated if (options.sort !== undefined) args.sort = options.sort if (options.search !== undefined) args.search = options.search if (options.page !== undefined) args.page = options.page if (options.json) args.outputType = 'json' const requestBody = JSON.stringify({ jsonrpc: '2.0', method: 'tools/call', params: { name: 'query_spans', arguments: args, }, id: 1, }) const res = await fetch(`http://127.0.0.1:${port}/mcp`, { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/event-stream', }, body: requestBody, }) const body = await res.text() for (const line of body.split('\n')) { if (!line.startsWith('data: ')) continue const msg = JSON.parse(line.slice('data: '.length)) const text = msg.result?.content?.find((c) => c.type === 'text')?.text if (text !== undefined) { process.stdout.write(text) return } } } ``` Sources: [packages/next/src/cli/internal/query-trace.ts:21-102](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/query-trace.ts#L21-L102) ### Query Parameters and Telemetry Reference | Parameter / Field | Type | Default Value | Description / Purpose | Sources | | :--- | :--- | :--- | :--- | :--- | | `parent` | `string` (Zod) | `undefined` | Span ID to enumerate children of; omit for root-level spans. | [packages/next/src/cli/internal/turbo-trace-server.ts:159-164](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L159-L164) | | `aggregated` | `boolean` (Zod) | `true` | Aggregate spans with the same name into a single entry when true. | [packages/next/src/cli/internal/turbo-trace-server.ts:165-170](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L165-L170) | | `sort` | `enum` (`value`, `name`) | `undefined` | Sort mode: `"value"` for corrected duration descending, `"name"` for alphabetical. | [packages/next/src/cli/internal/turbo-trace-server.ts:171-176](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L171-L176) | | `search` | `string` (Zod) | `undefined` | Substring search query applied to span name and category. | [packages/next/src/cli/internal/turbo-trace-server.ts:177-182](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L177-L182) | | `page` | `number` (Zod) | `1` | 1-based page number for paginated results (20 spans per page). | [packages/next/src/cli/internal/turbo-trace-server.ts:157](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L157), [packages/next/src/cli/internal/turbo-trace-server.ts:183-183](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L183-L183) | | `outputType` | `enum` (`markdown`, `json`) | `'markdown'` | Output format: human-readable markdown or structured JSON with raw memory samples. | [packages/next/src/cli/internal/turbo-trace-server.ts:157](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L157), [packages/next/src/cli/internal/turbo-trace-server.ts:184-189](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L184-L189) | ## Related - [[Dev Server and HMR]] --- ## Technical docs: Bundler Integration URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/development-and-diagnostics/bundler-integration
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/taskfile.js](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js) - [packages/next/src/server/dev/hot-reloader-webpack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts) - [packages/next/src/lib/bundler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/bundler.ts) - [packages/next/src/server/dev/hot-reloader-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts) - [packages/next/src/server/dev/on-demand-entry-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts) - [packages/next/next-runtime.webpack-config.js](https://github.com/blade47/next.js/blob/main/packages/next/next-runtime.webpack-config.js) - [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts) - [packages/next/src/server/config-shared.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts) - [packages/next/src/shared/lib/get-webpack-bundler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/get-webpack-bundler.ts) - [packages/next-bundle-analyzer/index.js](https://github.com/blade47/next.js/blob/main/packages/next-bundle-analyzer/index.js) - [packages/next/src/cli/next-build.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-build.ts) - [turbopack/packages/turbo-tracing-next-plugin/src/index.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/turbo-tracing-next-plugin/src/index.ts) - [packages/next/src/bundles/webpack/packages/webpack.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/webpack.js) - [packages/next/src/server/config.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts) - [packages/next-rspack/index.js](https://github.com/blade47/next.js/blob/main/packages/next-rspack/index.js) - [packages/next/src/server/next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts) - [packages/next/src/server/config-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts) - [packages/next/next-devtools.webpack-config.js](https://github.com/blade47/next.js/blob/main/packages/next/next-devtools.webpack-config.js) - [packages/next/src/server/lib/dev-bundler-service.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dev-bundler-service.ts) - [packages/next/src/lib/turbopack-warning.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts) - [packages/next/src/bin/next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/bin/next.ts) - [packages/next/src/telemetry/events/build.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/events/build.ts) - [packages/next/src/bundles/webpack/bundle5.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/bundle5.js) - [packages/next/src/server/dev/turbopack-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/turbopack-utils.ts) - [packages/next/src/shared/lib/turbopack/manifest-loader.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/manifest-loader.ts)
## Overview Next.js bundler integration coordinates compilation across multiple underlying bundler backends—specifically supporting Turbopack, Webpack, and Rspack. It manages CLI argument parsing, configuration validation, compiler lifecycle orchestration, and incremental manifest persistence for both development and production targets. Sources: [packages/next/src/lib/bundler.ts:2-87](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/bundler.ts#L2-L87), [packages/next/src/server/dev/hot-reloader-webpack.ts:687-787](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts#L687-L787), [packages/next/src/shared/lib/turbopack/manifest-loader.ts:177-225](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/manifest-loader.ts#L177-L225) ## Bundler Selection and Configuration Engine ### Overview The bundler selection and configuration engine is responsible for parsing command-line options, enumerating supported compilation backends, validating user configurations, and ensuring Turbopack compatibility against unsupported Next.js configuration options. It bridges CLI invocations and the core build system by determining which bundler engine (`Turbopack`, `Webpack`, or `Rspack`) executes for a given command. Sources: [packages/next/src/lib/bundler.ts:1-102](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/bundler.ts#L1-L102), [packages/next/src/cli/next-build.ts:13-74](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-build.ts#L13-L74), [packages/next/src/lib/turbopack-warning.ts:41-190](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L41-L190) ### Bundler Enumeration and CLI Argument Parsing The selection mechanism revolves around the `Bundler` enumeration and the `parseBundlerArgs` function located in `packages/next/src/lib/bundler.ts`. This engine evaluates explicit CLI flags, environment variables, and test overrides to select the active bundler backend. | Bundler Enum Member | Associated CLI Flags / Env Vars | Default Behavior / Side Effects | Sources | | :--- | :--- | :--- | :--- | | `Bundler.Turbopack` | `--turbopack`, `--turbo`, `TURBOPACK`, `IS_TURBOPACK_TEST` | Default when no flags are configured (`process.env.TURBOPACK = 'auto'`) | [packages/next/src/lib/bundler.ts:2-84](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/bundler.ts#L2-L41,L73-L84) | | `Bundler.Webpack` | `--webpack`, `IS_WEBPACK_TEST` | Sets webpack compilation pipeline when explicitly invoked | [packages/next/src/lib/bundler.ts:42-51](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/bundler.ts#L4-L4,L42-L51) | | `Bundler.Rspack` | `NEXT_RSPACK`, `NEXT_TEST_USE_RSPACK` | Configured via environment variable side-effects and Next.js config | [packages/next/src/lib/bundler.ts:53-101](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/bundler.ts#L5-L5,L53-L63,L95-L101) | Sources: [packages/next/src/lib/bundler.ts:2-87](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/bundler.ts#L2-L87) > [!CAUTION] > Setting conflicting flags across different bundlers (e.g., passing both `--turbopack` and `--webpack`) causes `parseBundlerArgs` to record multiple entries in `bundlerFlags`, print an error listing all active flags to `console.error`, and force an immediate termination via `process.exit(1)`. > Sources: [packages/next/src/lib/bundler.ts:65-72](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/bundler.ts#L65-L72) ### Configuration Validation and Turbopack Compatibility Checks Once the initial bundler is chosen, `validateTurboNextConfig` parses the user's `next.config.js` via `loadConfig` in raw configuration mode and checks for options that are incompatible with Turbopack. It recursively flattens custom configuration keys and compares them against `unsupportedTurbopackNextConfigOptions`. > [!WARNING] > If a build defaults to Turbopack (`process.env.TURBOPACK === 'auto'`) while a `webpack` configuration property is defined without a corresponding `turbopack` configuration, Next.js logs an error and terminates execution with `process.exit(1)`. Users can silence this check by supplying an explicit `--turbopack` or `--webpack` flag or by declaring an empty `turbopack: {}` block in their configuration file. > Sources: [packages/next/src/lib/turbopack-warning.ts:143-166](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L143-L166) The engine tracks a specific set of unsupported Next.js configuration keys when running under Turbopack: - `experimental.fetchCacheKeyPrefix` - `experimental.clientRouterFilterAllowedRate` - `experimental.allowedRevalidateHeaderKeys` - `experimental.extensionAlias` - `experimental.fallbackNodePolyfills` - `experimental.swcTraceProfiling` - `experimental.craCompat` - `experimental.disablePostcssPresetEnv` - `experimental.esmExternals` - `experimental.forceSwcTransforms` - `experimental.fullySpecified` - `experimental.urlImports` - `experimental.slowModuleDetection` Sources: [packages/next/src/lib/turbopack-warning.ts:5-38](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L5-L38) ### Execution Walkthrough The bundler selection and validation sequence flows through several stages: 1. **CLI Parsing**: `parseBundlerArgs(options)` evaluates `options.turbopack`, `options.turbo`, `options.webpack`, and associated environment variables (`TURBOPACK`, `NEXT_RSPACK`, etc.), populating `bundlerFlags`. 2. **Cardinality Check**: If `bundlerFlags.size > 1`, `parseBundlerArgs` rejects the command, logs the conflict, and exits with status `1`. If `bundlerFlags.size === 0`, it defaults to `Bundler.Turbopack` and sets `process.env.TURBOPACK = 'auto'`. 3. **Build Execution Entry**: `nextBuild()` invokes `parseBundlerArgs(options)` and performs secondary validations, such as asserting that `--experimental-analyze` matches exclusively with `Bundler.Turbopack`. 4. **Config Interrogation**: `validateTurboNextConfig` loads the raw configuration object via `loadConfig(configPhase, dir, { rawConfig: true })`, flattens its keys, and checks against the unsupported option manifest. Sources: [packages/next/src/lib/bundler.ts:15-87](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/bundler.ts#L15-L87), [packages/next/src/cli/next-build.ts:68-74](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-build.ts#L68-L74), [packages/next/src/lib/turbopack-warning.ts:41-76](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L41-L76) ## Development Bundler Orchestration Architecture ### Overview Development bundler orchestration bridges Next.js server initialization, dynamic route watching, and the underlying compiler lifecycle. The development server instantiates the bundler through `setupDevBundler`, recording telemetry events and returning a `DevBundler` interface that exposes request handlers, manifest checkers, and hot-reloading hooks. Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:1302-1341](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L1302-L1341), [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:1343-1343](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L1343) ### DevBundler Interface and Service Lifecycle The `DevBundlerService` wraps the `DevBundler` instance to perform development-time tasks, manage Incremental Static Regeneration (ISR) manifests via an internal `LRUCache`, and route HMR communication. Sources: [packages/next/src/server/lib/dev-bundler-service.ts:17-43](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dev-bundler-service.ts#L17-L43) | Property / Method | Source Interface / Target | Purpose | | :--- | :--- | :--- | | `appIsrManifestInner` | `LRUCache` | Stores ISR status flags for active routes with a maximum capacity of 8,000 entries. | | `close` | `NextJsHotReloaderInterface['close']` | Binds to hot reloader closure logic to tear down compilation watchers. | | `setCacheStatus` | `NextJsHotReloaderInterface['setCacheStatus']` | Updates cache status channels during compilation. | | `setReactDebugChannel` | `NextJsHotReloaderInterface['setReactDebugChannel']` | Configures React debugging message transmission. | | `sendErrorsToBrowser` | `NextJsHotReloaderInterface['sendErrorsToBrowser']` | Forwards compilation and build errors to connected clients. | | `ensurePage` | `DevBundler['hotReloader']['ensurePage']` | Triggers compilation of specific page definitions on demand. | Sources: [packages/next/src/server/lib/dev-bundler-service.ts:18-50](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dev-bundler-service.ts#L18-L50) > [!NOTE] > The ISR status manifest is selectively transmitted to legacy Pages Router clients or App Router clients with Cache Components disabled. When Cache Components are active, the binary nature of partial static rendering prevents the static indicator manifest from providing granular telemetry. > Sources: [packages/next/src/server/lib/dev-bundler-service.ts:113-130](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dev-bundler-service.ts#L113-L130) ### Service Mocking and Revalidation Pipeline `DevBundlerService` enables programmatic revalidation by mocking Node.js request and response objects, dispatching them through the worker handler, and asserting HTTP cache headers. Sources: [packages/next/src/server/lib/dev-bundler-service.ts:75-101](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dev-bundler-service.ts#L75-L101) ```typescript public async revalidate({ urlPath, headers, opts: revalidateOpts, }: { urlPath: string headers: IncomingMessage['headers'] opts: any }) { const mocked = createRequestResponseMocks({ url: urlPath, headers, }) await this.handler(mocked.req, mocked.res) await mocked.res.hasStreamed if ( mocked.res.getHeader('x-nextjs-cache') !== 'REVALIDATED' && mocked.res.statusCode !== 200 && !(mocked.res.statusCode === 404 && revalidateOpts.unstable_onlyGenerated) ) { throw new Error(`Invalid response ${mocked.res.statusCode}`) } return {} } ``` Sources: [packages/next/src/server/lib/dev-bundler-service.ts:75-101](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/dev-bundler-service.ts#L75-L101) ### Router Server Bridge and Virtual Manifests The setup routine registers virtual file system items (`devVirtualFsItems`) for client pages manifests and middleware matchers, intercepting incoming HTTP requests inside `requestHandler` before they reach downstream routing logic. Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:1221-1258](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L1221-L1258) The request handling sequence processes incoming HTTP messages through distinct stages: 1. **URL Parsing**: `requestHandler` invokes `parseUrl(req.url || '/')` to extract the request pathname. 2. **Manifest Interception**: If `pathname` includes `clientPagesManifestPath`, the server responds with status `200`, sets `Content-Type: application/json`, and serializes non-App Router routes filtered via `opts.fsChecker.appFiles`. 3. **Middleware Matcher Interception**: If `pathname` matches `devMiddlewareManifestPath` or `devTurbopackMiddlewareManifestPath`, it responds with `serverFields.middleware?.matchers` and returns `{ finished: true }`. 4. **Fallback Propagation**: If no virtual manifests match, it returns `{ finished: false }` to let standard routing handle the request. Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:1230-1258](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L1230-L1258) > [!WARNING] > When `logErrorWithOriginalStack` processes runtime errors, it deobfuscates error messages and checks instance types: `ModuleBuildError` logs standard error output via `Log.error(err.message)`, whereas `TurbopackInternalError` suppresses raw console output since rust-side handlers already write simplified messages to disk. > Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:1260-1282](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L1260-L1282) ## Webpack and Rspack Compilation Lifecycle ### Overview The Webpack and Rspack compilation lifecycle handles bundler resolution, runtime configuration generation for multi-compiler setups, hot reloader initialization, and Node.js require-hook patching. Next.js isolates its internal bundling dependencies by re-routing module requests through custom resolution layers and runtime configuration generators. Sources: [packages/next/src/server/dev/hot-reloader-webpack.ts:687-787](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts#L687-L787), [packages/next/src/shared/lib/get-webpack-bundler.ts:1-12](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/get-webpack-bundler.ts#L1-L12), [packages/next/src/server/config-utils.ts:1-144](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L1-L144) ### Bundler Resolution and Hook Patching Next.js provides a unified access layer for selecting between standard Webpack and Rspack via `getWebpackBundler()`. When `process.env.NEXT_RSPACK` is active, it loads Rspack core via `getRspackCore()`; otherwise, it returns standard Webpack. ```typescript export default function getWebpackBundler(): typeof webpack { return process.env.NEXT_RSPACK ? getRspackCore() : webpack } ``` Sources: [packages/next/src/shared/lib/get-webpack-bundler.ts:1-12](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/get-webpack-bundler.ts#L1-L12), [packages/next/src/bundles/webpack/packages/webpack.js:5-11](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/webpack.js#L5-L11) To prevent version mismatch issues with user-installed packages, `loadWebpackHook()` patches the Node.js `require` function to route `webpack` and internal loader requests directly to Next.js's bundled webpack versions and plugins. ```typescript export function loadWebpackHook() { if (installed) { return } installed = true ;( require('../server/require-hook') as typeof import('../server/require-hook') ).addHookAliases( [ ['webpack', 'next/dist/compiled/webpack/webpack-lib'], ['webpack/package', 'next/dist/compiled/webpack/package'], ['webpack/package.json', 'next/dist/compiled/webpack/package'], ['webpack/lib/webpack', 'next/dist/compiled/webpack/webpack-lib'], ['webpack/lib/webpack.js', 'next/dist/compiled/webpack/webpack-lib'], [ 'webpack/lib/node/NodeEnvironmentPlugin', 'next/dist/compiled/webpack/NodeEnvironmentPlugin', ], ].map( ([request, replacement]) => [request, require.resolve(replacement)] ) ) } ``` Sources: [packages/next/src/server/config-utils.ts:1-144](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L1-L144) > [!NOTE] > `loadWebpackHook()` uses dynamic `require.resolve` lookups mapped over array pairs to ensure replacement targets resolve to valid built artifacts within `next/dist/compiled/webpack/`. > Sources: [packages/next/src/server/config-utils.ts:1-144](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L1-L144) ### Runtime Webpack Configuration Generation The `getWebpackConfig` method orchestrates the creation of multi-compiler configurations for client, server, and edge-server runtimes. The configuration generation sequence proceeds through distinct phases: 1. **Page Discovery**: `getWebpackConfig` executes `findPageFile` concurrently for `/_app` and `/_document` files if a pages directory exists. 2. **Mapping Creation**: It invokes `createPagesMapping` to build page definitions using `PAGE_TYPES.PAGES`. 3. **Entrypoint Generation**: It calls `createEntrypoints` passing the collected pages, app directory state, and preview configuration properties. 4. **Project Info Loading**: It retrieves project details via `loadProjectInfo`. 5. **Compiler Generation**: It resolves base configurations concurrently for client, server, and edge-server compilers using `getBaseWebpackConfig`. Sources: [packages/next/src/server/dev/hot-reloader-webpack.ts:687-787](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts#L687-L787) ```typescript private async getWebpackConfig(span: Span) { const webpackConfigSpan = span.traceChild('get-webpack-config') const pageExtensions = this.config.pageExtensions return webpackConfigSpan.traceAsyncFn(async () => { const pagePaths = !this.pagesDir ? ([] as (string | null)[]) : await webpackConfigSpan .traceChild('get-page-paths') .traceAsyncFn(() => Promise.all([ findPageFile(this.pagesDir!, '/_app', pageExtensions, false), findPageFile(this.pagesDir!, '/_document', pageExtensions, false), ]) ) this.pagesMapping = await webpackConfigSpan .traceChild('create-pages-mapping') .traceAsyncFn(() => createPagesMapping({ isDev: true, pageExtensions: this.config.pageExtensions, pagesType: PAGE_TYPES.PAGES, pagePaths: pagePaths.filter( (i: string | null): i is string => typeof i === 'string' ), pagesDir: this.pagesDir, appDir: this.appDir, appDirOnly: Boolean(this.appDir && !this.pagesDir), }) ) const entrypoints = await webpackConfigSpan .traceChild('create-entrypoints') .traceAsyncFn(() => createEntrypoints({ appDir: this.appDir, buildId: this.buildId, config: this.config, envFiles: [], isDev: true, pages: this.pagesMapping, pagesDir: this.pagesDir, previewMode: this.previewProps, rootDir: this.dir, pageExtensions: this.config.pageExtensions, }) ) const commonWebpackOptions = { dev: true, buildId: this.buildId, encryptionKey: this.encryptionKey, config: this.config, pagesDir: this.pagesDir, rewrites: this.rewrites, originalRewrites: this.config._originalRewrites, originalRedirects: this.config._originalRedirects, runWebpackSpan: this.hotReloaderSpan, appDir: this.appDir, previewProps: this.previewProps, } return webpackConfigSpan .traceChild('generate-webpack-config') .traceAsyncFn(async () => { const info = await loadProjectInfo({ dir: this.dir, config: commonWebpackOptions.config, dev: true, }) return Promise.all([ getBaseWebpackConfig(this.dir, { ...commonWebpackOptions, compilerType: COMPILER_NAMES.client, entrypoints: entrypoints.client, ...info, }), getBaseWebpackConfig(this.dir, { ...commonWebpackOptions, compilerType: COMPILER_NAMES.server, entrypoints: entrypoints.server, ...info, }), getBaseWebpackConfig(this.dir, { ...commonWebpackOptions, compilerType: COMPILER_NAMES.edgeServer, entrypoints: entrypoints.edgeServer, ...info, }), ]) }) }) } ``` Sources: [packages/next/src/server/dev/hot-reloader-webpack.ts:687-787](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts#L687-L787) ### Design Trade-Offs and Constants | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | **Node require-hook patching** | Prevents conflicting user-land dependencies and ensures exact Webpack version alignment. | Mutates global Node.js require behavior, which can interfere with advanced custom module loaders. | | **Multi-compiler generation (`client`, `server`, `edgeServer`)** | Isolates environment-specific transformations (such as SSR vs. browser code). | Increases initial configuration generation time and memory footprint during startup. | | **Virtual CommonJS package output (`{"type": "commonjs"}`)** | Forces deterministic CommonJS parsing rules inside the `.next` output directory. | Restricts native ESM feature usage inside internal build outputs. | Sources: [packages/next/src/server/dev/hot-reloader-webpack.ts:886-897](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts#L886-L897), [packages/next/src/server/config-utils.ts:1-144](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L1-L144) ## On-Demand Entrypoint Resolution Pipeline ### Overview The on-demand entrypoint resolution pipeline governs lazy page compilation scheduling, inactive entry disposal, and batch invalidation control during development. The `Invalidator` class coordinates compiler triggering to ensure that concurrent invalidation requests are batched without forcing unintended client-side hard reloads due to unstable Webpack hashes. Sources: [packages/next/src/server/dev/on-demand-entry-handler.ts:270-326](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L270-L326) ### Invalidation Batching and Execution Flow The `Invalidator` class manages compilation states using `building` and `rebuildAgain` trackers (`BuildingTracker` and `RebuildTracker`, mapped to `CompilerNameValues`). When an invalidation is requested via `invalidate(compilerKeys)`, the execution pipeline follows a precise conditional sequence: ``` invalidate(compilerKeys) → checks if compilerKey is in building Set → [If building] adds key to rebuildAgain Set and continues → [If idle] adds key to building Set → calls multiCompiler.compilers[COMPILER_INDEXES[key]].watching?.invalidate() ``` When compilation completes, `doneBuilding(compilerKeys)` clears the keys from `building` and checks `rebuildAgain`, automatically re-triggering `invalidate(rebuild)` if queued updates exist. Sources: [packages/next/src/server/dev/on-demand-entry-handler.ts:272-326](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L272-L326) > [!NOTE] > If a build is actively processing a compiler key when an invalidation arrives, `Invalidator` never aborts the active build. Aborting an active build would trigger a client-side hard reload; instead, the key is registered in `rebuildAgain` and flushed immediately upon completion. > Sources: [packages/next/src/server/dev/on-demand-entry-handler.ts:286-296](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L286-L296) ### Entrypoint Lifecycle and Path Resolution Active entries are tracked through `entriesMap`, which indexes entry objects by output directory and entry name. The `findPagePathData` function normalizes page routes and resolves them to absolute paths using project extensions and directory configurations. ```typescript export async function findPagePathData( rootDir: string, page: string, extensions: string[], pagesDir: string | undefined, appDir: string | undefined, isGlobalNotFoundEnabled: boolean ): Promise ``` Sources: [packages/next/src/server/dev/on-demand-entry-handler.ts:233-254](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L233-L254), [packages/next/src/server/dev/on-demand-entry-handler.ts:400-408](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L400-L408) Inactive entries are periodically evaluated by `disposeInactiveEntries`, which flags entries for removal if their `lastActiveTime` exceeds `maxInactiveAge`. Root middleware, instrumentation hooks, and currently active client or server access pages are explicitly excluded from periodic disposal. Sources: [packages/next/src/server/dev/on-demand-entry-handler.ts:328-371](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L328-L371) > [!WARNING] > Middleware and instrumentation hook files identified by `isMiddlewareFilename(bundlePath)` or `isInstrumentationHookFilename(bundlePath)` are permanently exempt from periodic inactive disposal. Disposing them would break request handling for subsequent requests requiring these handlers. > Sources: [packages/next/src/server/dev/on-demand-entry-handler.ts:342-348](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts#L342-L348) ## Turbopack Runtime and Manifest Operations ### Overview The Turbopack runtime and manifest subsystem manages route dispatching, HMR event streaming, issue tracking, and incremental persistence of build artifacts. The `TurbopackManifestLoader` class coordinates write operations across build, page, client build, app paths, action, font, middleware, and subresource integrity manifests using an internal change-tracking cache layer (`ManifestsMap`). Sources: [packages/next/src/server/dev/turbopack-utils.ts:146-150](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/turbopack-utils.ts#L146-L150), [packages/next/src/shared/lib/turbopack/manifest-loader.ts:134-175](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/manifest-loader.ts#L134-L175) ### Route Type Handling and Call Chain Route compilation and dispatching are executed via `handleRouteType`, which processes different route variants such as `'page'`, `'page-api'`, `'app-page'`, and `'app-route'`. When loading middleware or server references for these routes, path resolution relies on precise manifest lookup sequences. ```mermaid sequenceDiagram participant H as handleRouteType participant M as loadMiddlewareManifest participant P as getManifestPath participant A as addMetadataIdToRoute participant S as addRouteSuffix participant L as loadPagesManifest participant SM as ManifestsMap.set participant GT as ManifestsMap.get H->>M: loadMiddlewareManifest(pageName, type) M->>P: getManifestPath(page, distDir, name, type, true) P->>A: addMetadataIdToRoute(basePage) P->>S: addRouteSuffix(...) H->>L: loadPagesManifest(pageName) L->>SM: set(key, value) SM->>GT: get(key) ``` Sources: [packages/next/src/server/dev/turbopack-utils.ts:178-434](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/turbopack-utils.ts#L178-L434), [packages/next/src/shared/lib/turbopack/manifest-loader.ts:71-118](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/manifest-loader.ts#L71-L118) 1. `handleRouteType` invokes `loadMiddlewareManifest` to resolve edge runtimes and associated entry points. Sources: [packages/next/src/server/dev/turbopack-utils.ts:237-238](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/turbopack-utils.ts#L237-L238), [packages/next/src/shared/lib/turbopack/manifest-loader.ts:657-667](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/manifest-loader.ts#L657-L667) 2. `loadMiddlewareManifest` calls `getManifestPath` to locate the target artifact on disk. Sources: [packages/next/src/shared/lib/turbopack/manifest-loader.ts:661-667](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/manifest-loader.ts#L661-L667) 3. `getManifestPath` invokes `addMetadataIdToRoute` to format metadata route file paths. Sources: [packages/next/src/shared/lib/turbopack/manifest-loader.ts:113-113](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/manifest-loader.ts#L113) 4. `getManifestPath` invokes `addRouteSuffix` to append the required route file boundary suffix. Sources: [packages/next/src/shared/lib/turbopack/manifest-loader.ts:113-113](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/manifest-loader.ts#L113) 5. `handleRouteType` invokes `loadPagesManifest` to record server page mappings. Sources: [packages/next/src/server/dev/turbopack-utils.ts:204-204](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/turbopack-utils.ts#L204), [packages/next/src/shared/lib/turbopack/manifest-loader.ts:817-822](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/manifest-loader.ts#L817-L822) 6. `loadPagesManifest` delegates to `ManifestsMap.set` to update raw and parsed json objects. Sources: [packages/next/src/shared/lib/turbopack/manifest-loader.ts:818-818](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/manifest-loader.ts#L818), [packages/next/src/shared/lib/turbopack/manifest-loader.ts:141-145](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/turbopack/manifest-loader.ts#L141-L145) 7. `ManifestsMap.set` uses `ManifestsMap.get` to evaluate existing map state during updates. Sources: ## Related - [[Dev Server and HMR]] - [[React Refresh Support]] --- ## Technical docs: React Refresh Support URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/development-and-diagnostics/react-refresh-support
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/dev/hot-reloader-webpack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts) - [packages/react-refresh-utils/internal/RspackReactRefresh.ts](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/internal/RspackReactRefresh.ts) - [packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts) - [packages/next/src/server/dev/hot-reloader-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts) - [packages/react-refresh-utils/ReactRefreshRspackPlugin.ts](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/ReactRefreshRspackPlugin.ts) - [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx) - [packages/react-refresh-utils/internal/ReactRefreshModule.runtime.ts](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/internal/ReactRefreshModule.runtime.ts) - [packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts) - [packages/react-refresh-utils/internal/helpers.ts](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/internal/helpers.ts) - [packages/next/src/server/dev/on-demand-entry-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts) - [packages/next/src/bundles/webpack/packages/HotModuleReplacement.runtime.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/HotModuleReplacement.runtime.js) - [packages/react-refresh-utils/rspack-runtime.ts](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/rspack-runtime.ts) - [packages/react-refresh-utils/loader.ts](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/loader.ts) - [packages/next/src/bundles/webpack/packages/JavascriptHotModuleReplacement.runtime.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/JavascriptHotModuleReplacement.runtime.js) - [packages/react-refresh-utils/runtime.ts](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/runtime.ts) - [packages/react-refresh-utils/react-refresh-runtime.d.ts](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/react-refresh-runtime.d.ts) - [packages/next/src/server/dev/hot-reloader-rspack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-rspack.ts) - [packages/next/src/bundles/webpack/packages/lazy-compilation-web.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/lazy-compilation-web.js) - [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts) - [packages/next/next-devtools.webpack-config.js](https://github.com/blade47/next.js/blob/main/packages/next/next-devtools.webpack-config.js) - [packages/next/src/client/dev/hot-reloader/turbopack-hot-reloader-common.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/turbopack-hot-reloader-common.ts) - [packages/react-refresh-utils/tsconfig.json](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/tsconfig.json) - [packages/next/src/client/dev/hot-reloader/shared.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/shared.ts) - [packages/next/src/client/dev/noop-turbopack-hmr.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/noop-turbopack-hmr.ts) - [packages/react-refresh-utils/package.json](https://github.com/blade47/next.js/package.json)
## Overview React Refresh Support (commonly known as Fast Refresh) in Next.js provides instant feedback during local development by updating React components in the browser without losing component state. The system bridges lower-level Hot Module Replacement (HMR) mechanisms provided by Webpack and Rspack with the official `react-refresh` runtime library. By instrumenting module execution, registering React component families, and tracking exports signatures, the framework can surgically refresh updated components while falling back to full reloads when signature changes or side effects violate safety invariants. Sources: [packages/react-refresh-utils/package.json:1-32](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/package.json#L1-L32) The subsystem consists of three major structural pillars: runtime integration utilities (`@next/react-refresh-utils`), bundler-specific plugins (`ReactFreshWebpackPlugin` for Webpack and `ReactRefreshRspackPlugin` for Rspack), and client-side HMR reconciliation handlers (`hot-reloader-app.tsx`, `hot-reloader-pages.ts`). These layers cooperate to intercept module execution, evaluate whether exported entities qualify as React refresh boundaries, schedule updates via `module.hot`, and communicate status changes to the developer overlay. Sources: [packages/react-refresh-utils/runtime.ts:1-35](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/runtime.ts#L1-L35) --- ## Bundler Integration and Webpack Plugins The `ReactFreshWebpackPlugin` handles Webpack 4 and Webpack 5 integrations by hooking into compilation lifecycles to inject necessary runtime helpers and intercept module execution. For Webpack 5, the plugin appends a `ReactRefreshRuntimeModule` to runtime requirements via `compilation.hooks.additionalTreeRuntimeRequirements`. Sources: [packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts:88-102](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts#L88-L102) This runtime module hooks into `RuntimeGlobals.interceptModuleExecution` to wrap original module factories. Sources: [packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts:98-107](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts#L98-L107) When a module executes, the wrapper establishes execution interception via `self.$RefreshInterceptModuleExecution$(moduleId)`. This temporary hook re-binds `self.$RefreshReg$` and `self.$RefreshSig$` to register component types and transform signature functions against the specific `webpackModuleId`. A `try...finally` block guarantees cleanup even if module execution throws an error. Sources: [packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts:118-135](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts#L118-L135) ```typescript // Example wiring in Webpack 5 compilation hook compiler.hooks.compilation.tap('ReactFreshWebpackPlugin', (compilation) => { injectRefreshFunctions(compilation, Template) compilation.hooks.additionalTreeRuntimeRequirements.tap( 'ReactFreshWebpackPlugin', (chunk: any) => { compilation.addRuntimeModule(chunk, new ReactRefreshRuntimeModule()) } ) }) ``` Sources: [packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts:144-153](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts#L144-L153) > [!NOTE] > Webpack 4 lacks a native module execution interception API, so `ReactFreshWebpackPlugin` inspects and rewrites the source code of the template's require/evaluation block using string matching on `modules[moduleId].call(`. Sources: [packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts:33-85](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/ReactRefreshWebpackPlugin.ts#L33-L85) --- ## Rspack Runtime and Plugin Architecture For Rspack environments, `ReactRefreshRspackPlugin` injects `$ReactRefreshRuntime$` globally via Rspack's `ProvidePlugin` and ensures `RuntimeGlobals.moduleCache` is present in the compilation's runtime requirements. Sources: [packages/react-refresh-utils/ReactRefreshRspackPlugin.ts:5-20](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/ReactRefreshRspackPlugin.ts#L5-L20) The companion runtime module (`rspack-runtime.ts`) initializes `react-refresh/runtime` against `self` using `RefreshRuntime.injectIntoGlobalHook(self)`, while assigning stub functions for `$RefreshSig$` and `$RefreshReg$` to prevent reference errors in uninstrumented modules. Sources: [packages/react-refresh-utils/rspack-runtime.ts:6-19](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/rspack-runtime.ts#L6-L19) ```mermaid flowchart TD A["Rspack Compilation Start"] --> B["ProvidePlugin: inject $ReactRefreshRuntime$"] B --> C["Tap additionalTreeRuntimeRequirements"] C --> D["Add moduleCache requirement"] D --> E["Initialize RefreshRuntime.injectIntoGlobalHook(self)"] ``` Sources: [packages/react-refresh-utils/ReactRefreshRspackPlugin.ts:8-21](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/ReactRefreshRspackPlugin.ts#L8-L21) --- ## Module Loader and Runtime Transformation The `@next/react-refresh-utils` loader (`loader.ts`) appends module-level refresh execution logic to compiled `.ts`, `.tsx`, and `.js` files. It embeds `ReactRefreshModule.runtime.ts`, adapting `global.importMeta` to `import.meta` or `module.hot` depending on whether the file is CommonJS or ES module syntax. Sources: [packages/react-refresh-utils/loader.ts:1-16](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/loader.ts#L1-L16) The loader processes source files by calling `this.callback` with the original source combined with the un-wrapped runtime code. Sources: [packages/react-refresh-utils/loader.ts:18-32](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/loader.ts#L18-L32) The injected module runtime checks whether `self.$RefreshHelpers$` is available. If present, it retrieves `__webpack_module__.exports` and compares the previous module signature (`__webpack_module__.hot.data?.prevSignature`) against the current signature. Sources: [packages/react-refresh-utils/internal/ReactRefreshModule.runtime.ts:25-35](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/internal/ReactRefreshModule.runtime.ts#L25-L35) ```typescript // Runtime module injection sequence var currentExports = __webpack_module__.exports var prevSignature = __webpack_module__.hot.data?.prevSignature ?? null self.$RefreshHelpers$.registerExportsForReactRefresh(currentExports, __webpack_module__.id) if (self.$RefreshHelpers$.isReactRefreshBoundary(currentExports)) { __webpack_module__.hot.dispose(function (data) { data.prevSignature = self.$RefreshHelpers$.getRefreshBoundarySignature(currentExports) }) global.importMeta.webpackHot.accept() } ``` Sources: [packages/react-refresh-utils/internal/ReactRefreshModule.runtime.ts:31-56](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/internal/ReactRefreshModule.runtime.ts#L31-L56) --- ## Refresh Boundaries and Invalidation Logic Helpers in `packages/react-refresh-utils/internal/helpers.ts` determine whether a module constitutes a valid React Refresh boundary via `isReactRefreshBoundary()`. A module is a boundary if it exports components likely to be React components or if all its non-safe exports qualify as component types. Sources: [packages/react-refresh-utils/internal/helpers.ts:111-137](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/internal/helpers.ts#L111-L137) Safe exports (`__esModule`, `__N_SSG`, `__N_SSP`, and `config`) are ignored during inspection. Sources: [packages/react-refresh-utils/internal/helpers.ts:52-60](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/internal/helpers.ts#L52-L60) When an update occurs, `shouldInvalidateReactRefreshBoundary()` compares signature arrays. If the length or any element differs between `prevSignature` and `nextSignature`, the boundary is invalidated via `webpackHot.invalidate()`, triggering a wider reload cascade. Sources: [packages/react-refresh-utils/internal/helpers.ts:139-152](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/internal/helpers.ts#L139-L152) Otherwise, `scheduleUpdate()` batches updates for execution via `RefreshRuntime.performReactRefresh()`. Sources: [packages/react-refresh-utils/internal/helpers.ts:154-176](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/internal/helpers.ts#L154-L176) | Helper Function | Purpose | Condition / Return Value | | :--- | :--- | :--- | | `isSafeExport` | Filters framework metadata exports | Returns `true` for `__esModule`, `config`, etc. | | `isReactRefreshBoundary` | Evaluates module export eligibility | Returns `true` if all exports are components | | `shouldInvalidateReactRefreshBoundary` | Compares pre- and post-update signatures | Returns `true` if signature length or items diverge | | `scheduleUpdate` | Batches and coordinates update triggers | Invokes `RefreshRuntime.performReactRefresh()` when idle | Sources: [packages/react-refresh-utils/internal/helpers.ts:52-176](https://github.com/blade47/next.js/blob/main/packages/react-refresh-utils/internal/helpers.ts#L52-L176) > [!CAUTION] > If a file exports both React components and non-component utilities consumed outside the React tree, editing it will cause Fast Refresh to trigger a full page reload (`REACT_REFRESH_FULL_RELOAD`). Sources: [packages/next/src/client/dev/hot-reloader/shared.ts:3-10](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/shared.ts#L3-L10) --- ## Client-Side HMR Lifecycle and Error Handling Client entrypoints (`hot-reloader-app.tsx` and `hot-reloader-pages.ts`) manage communication with the HMR server, monitor compilation hashes against `__webpack_hash__`, and apply updates. Sources: [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:84-103](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx#L84-L103) When updates arrive, `tryApplyUpdatesWebpack()` verifies that `module.hot.status() === 'idle'`. If updates cannot be applied immediately, `afterApplyUpdates()` registers a status handler to defer execution until the HMR status returns to `'idle'`. Sources: [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:106-121](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx#L106-L121) If runtime errors occur (`RuntimeErrorHandler.hadRuntimeError`), or if update application fails, `performFullReload()` logs stack traces and calls `window.location.reload()`. Sources: [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:123-145](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx#L123-L145) ```typescript // Webpack hot update application flow function tryApplyUpdatesWebpack(sendMessage: (message: string) => void) { if (!isUpdateAvailable() || !canApplyUpdates()) { resolvePendingHotUpdateWebpack() dispatcher.onBuildOk() return } module.hot.check(true, function(err, updatedModules) { if (err || RuntimeErrorHandler.hadRuntimeError || updatedModules == null) { performFullReload(err, sendMessage) return } dispatcher.onRefresh() }) } ``` Sources: [packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx:148-179](https://github.com/blade47/next.js/blob/main/packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx#L148-L179) --- ## Rspack Persistent Cache and Built Entries Preservation `HotReloaderRspack` extends `HotReloaderWebpack` to solve issues with Rspack's persistent caching model. While Webpack updates modules incrementally, Rspack operates on complete module graph snapshots. Sources: [packages/next/src/server/dev/hot-reloader-rspack.ts:11-28](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-rspack.ts#L11-L28) To prevent Rspack from purging the module graph on server restart, `HotReloaderRspack` maintains a `built-entries.json` cache file under the `dist/cache/rspack/` directory. Sources: [packages/next/src/server/dev/hot-reloader-rspack.ts:29-69](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-rspack.ts#L29-L69) During `afterCompile`, built entry files are read from cache, verified for existence and content hash changes via `calculateFileHash()`, and restored into the active compiler's entry map. Sources: [packages/next/src/server/dev/hot-reloader-rspack.ts:70-139](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-rspack.ts#L70-L139) ```typescript // Calculating file hash for Rspack persistent entry cache validation async function calculateFileHash( filePath: string, algorithm: string = 'sha256' ): Promise { if (!(await fs.access(filePath).then(() => true, () => false))) { return } const fileBuffer = await fs.readFile(filePath) const hash = createHash(algorithm) hash.update(fileBuffer) return hash.digest('hex') } ``` Sources: [packages/next/src/server/dev/hot-reloader-rspack.ts:227-243](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-rspack.ts#L227-L243) > [!WARNING] > If a file listed in `built-entries.json` has been modified or deleted on disk since the last server stop, its hash validation will fail, causing the entry to be dropped from the restored cache to maintain graph integrity. Sources: [packages/next/src/server/dev/hot-reloader-rspack.ts:91-136](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-rspack.ts#L91-L136) ## Related - [[Bundler Integration]] - [[Dev Server and HMR]] --- ## Technical docs: Configuration Loading URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/build-and-config/configuration-loading
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/config.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts) - [packages/next/src/server/config-schema.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-schema.ts) - [packages/next/src/server/config-shared.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts) - [packages/next/src/lib/load-custom-routes.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts) - [packages/next/src/lib/find-config.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-config.ts) - [packages/next/src/next-devtools/server/devtools-config-middleware.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/server/devtools-config-middleware.ts) - [packages/next/src/server/lib/start-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/start-server.ts) - [packages/next/src/server/load-components.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/load-components.ts) - [packages/next-env/index.ts](https://github.com/blade47/next-env/index.ts) - [packages/next/src/server/next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next.ts) - [packages/next/src/server/load-manifest.external.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/load-manifest.external.ts) - [packages/next/src/lib/turbopack-warning.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts) - [packages/next/src/next-devtools/shared/devtools-config-schema.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/shared/devtools-config-schema.ts) - [packages/next/constants.js](https://github.com/blade47/next.js/blob/main/packages/next/constants.js) - [packages/next/src/shared/lib/constants.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/constants.ts) - [packages/next/src/server/config-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts) - [packages/next/src/export/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/export/index.ts) - [packages/next/src/server/require-hook.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/require-hook.ts) - [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts)
## Overview Configuration loading in Next.js is responsible for discovering, transpiling, validating, and normalizing project configuration files (`next.config.js`, `next.config.mjs`, `next.config.ts`, and `next.config.mts`) across different runtime phases. It bridges user-defined settings with internal framework defaults, enforces strict Zod schema validation, and processes advanced features such as custom HTTP routes, experimental flags, and bundler compatibility checks before initializing server processes. Sources: [packages/next/src/server/config.ts:1-60](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L1-L60), [packages/next/src/server/config-schema.ts:459-460](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-schema.ts#L459-L460), [packages/next/src/shared/lib/constants.ts:109-116](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/constants.ts#L109-L116) ## Config File Discovery and Resolution Configuration discovery and loading operates by searching upward from a target directory for recognized configuration files using `findUp` and executing appropriate module loaders based on file extension and test environment constraints. The process resolves files defined by `CONFIG_FILES` or custom key lookups, supporting TypeScript transpilation, dynamic ESM imports, and CommonJS requires. Sources: [packages/next/src/server/config.ts:4-9](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L4-L9), [packages/next/src/shared/lib/constants.ts:109-116](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/constants.ts#L109-L116) The configuration loading execution path follows a strict sequence from directory traversal to module evaluation and normalization: 1. `findUp(CONFIG_FILES, { cwd: dir })` scans parent directories for configuration filenames starting from the provided working directory `dir`. 2. `basename(path)` extracts the specific `configFileName` when a path match is found. 3. Module evaluation branches depending on testing modes and file types: - If `process.env.__NEXT_TEST_MODE === 'jest'`, `require(path)` executes because dynamic `import()` is unsupported inside Jest VM contexts. - If `configFileName === 'next.config.ts'`, `transpileConfig({ nextConfigPath: path, dir })` compiles the TypeScript configuration file. - Otherwise, `import(pathToFileURL(path).href)` dynamically imports the module using URL-escaped file paths. 4. `normalizeConfig(phase, interopDefault(userConfigModule))` processes the exported user configuration, executing exported config functions if present. Sources: [packages/next/src/server/config.ts:1861-1920](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L1861-L1920) > [!NOTE] > During Jest test execution (`process.env.__NEXT_TEST_MODE === 'jest'`), dynamic `import()` calls are bypassed in favor of synchronous `require(path)` to prevent Node.js VM context incompatibilities. Sources: [packages/next/src/server/config.ts:1875-1879](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L1875-L1879) In addition to primary Next.js server configuration discovery, the utility helper `findConfig` queries `package.json` configurations or falls back to known configuration file patterns via `findConfigPath`. Sources: [packages/next/src/lib/find-config.ts:10-38](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-config.ts#L10-L38) | Filename Pattern | Loader Type | Parsing Method | Sources | |------------------|-------------|----------------|---------| | `package.json` | JSON property lookup | `JSON.parse` with `packageJson[key]` extraction | [packages/next/src/lib/find-config.ts:40-60](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-config.ts#L40-L60) | | `*.config.js` | Conditional (ESM / CJS) | `import()` if `package.json` specifies `type: "module"`, otherwise `require()` | [packages/next/src/lib/find-config.ts:81-86](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-config.ts#L81-L86) | | `*.config.mjs` | ES Module | `import()` with `pathToFileURL` mapping on Windows | [packages/next/src/lib/find-config.ts:87-88](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-config.ts#L87-L88) | | `*.config.cjs` | CommonJS | `require()` | [packages/next/src/lib/find-config.ts:89-90](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-config.ts#L89-L90) | | JSON5 formats (`.*rc.json`, `*.config.json`, etc.) | JSON5 | `JSON5.parse` supporting inline comments | [packages/next/src/lib/find-config.ts:93-96](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-config.ts#L93-L96) | Sources: [packages/next/src/lib/find-config.ts:40-97](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-config.ts#L40-L97) > [!WARNING] > Windows absolute paths passed to dynamic ESM imports require explicit `file://` protocol prefixing via `pathToFileURL()` unless executing within Jest worker environments. Sources: [packages/next/src/server/config.ts:1871-1874](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L1871-L1874), [packages/next/src/lib/find-config.ts:68-78](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/find-config.ts#L68-L78) ## Schema Validation and Error Formatting Next.js validates finalized configuration objects against comprehensive Zod schemas defined in `config-schema.ts`. When validation errors occur, diagnostic messages are normalized via `normalizeNextConfigZodErrors` to separate warning diagnostics from fatal build errors. Sources: [packages/next/src/server/config.ts:60-110](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L60-L110), [packages/next/src/server/config-schema.ts:1-6](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-schema.ts#L1-L6) The master validation schema `configSchema` is constructed using `z.lazy()` around a strict object (`z.strictObject`) to disallow unexpected top-level properties. Specialized sub-schemas validate routing configurations, custom image loaders, and experimental features. Sources: [packages/next/src/server/config-schema.ts:459-460](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-schema.ts#L459-L460) | Schema Identifier | Zod Definition Type | Purpose & Validation Rules | Sources | |-------------------|---------------------|----------------------------|---------| | `zRouteHas` | Union (`z.union`) | Validates custom route conditions matching `header`, `query`, `cookie` (with key and optional value) or `host` (value only). | [packages/next/src/server/config-schema.ts:49-60](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-schema.ts#L49-L60) | | `zRewrite` | Strict Object (`z.strictObject`) | Validates rewrite rules containing `source`, `destination`, optional `basePath`, `locale`, `has`, `missing`, and `internal`. | [packages/next/src/server/config-schema.ts:62-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-schema.ts#L62-L70) | | `zRedirect` | Object intersection (`z.and`) | Validates redirects, ensuring either `permanent` boolean or `statusCode` number is set exclusively without conflicts. | [packages/next/src/server/config-schema.ts:72-93](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-schema.ts#L72-L93) | | `zHeader` | Strict Object (`z.strictObject`) | Validates custom response headers with array of `key`/`value` header objects and optional routing constraints. | [packages/next/src/server/config-schema.ts:95-104](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-schema.ts#L95-L104) | | `zTurbopackLoaderBuiltinCondition` | Enum union (`z.union`) | Restricts Turbopack loader builtin conditions to `browser`, `foreign`, `development`, `production`, `node`, or `edge-light`. | [packages/next/src/server/config-schema.ts:115-123](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-schema.ts#L115-L123) | Sources: [packages/next/src/server/config-schema.ts:49-123](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-schema.ts#L49-L123) > [!CAUTION] > Master validation enforces a strict object schema via `z.strictObject()`. Any unrecognized top-level properties supplied in `next.config.js` will trigger validation failure issues. Sources: [packages/next/src/server/config-schema.ts:459-460](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-schema.ts#L459-L460) When configuration validation runs, Zod issues are caught and processed by `normalizeNextConfigZodErrors` to determine whether diagnostics should halt the build or merely log warnings. Sources: [packages/next/src/server/config.ts:60-110](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L60-L110) The diagnostic normalization call-chain executes as follows: `validateConfigSchema()` → `normalizeZodErrors(error)` → loops through validation issues to inspect `issue.path` and `issue.code` → applies targeted deprecation or migration advice → pushes items into `fatalErrors` or `warnings` arrays based on `shouldExit`. Sources: [packages/next/src/server/config.ts:1958-1970](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L1958-L1970) | Error Condition / Unrecognized Key | Target Property Path | Action Taken & Migration Notice | Sources | |-------------------------------------|----------------------|---------------------------------|---------| | Image config error | `issue.path[0] === 'images'` | Sets `shouldExit = true`, treating the image configuration issue as a fatal error that terminates the build. | [packages/next/src/server/config.ts:71-74](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L71-L74) | | `turbopackPersistentCachingForBuild` | `issue.path[0] === 'experimental'` | Sets `shouldExit = true` and appends migration message directing users to `experimental.turbopackFileSystemCacheForBuild`. | [packages/next/src/server/config.ts:75-85](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L75-L85) | | `turbopackPersistentCaching` | `issue.path[0] === 'experimental'` | Sets `shouldExit = true` and appends migration message directing users to `experimental.turbopackFileSystemCacheForDev`. | [packages/next/src/server/config.ts:86-92](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L86-L92) | | `dynamicIO` | `issue.path[0] === 'experimental'` | Sets `shouldExit = true` and appends migration message replacing `dynamicIO` with `cacheComponents`. | [packages/next/src/server/config.ts:93-100](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L93-L100) | Sources: [packages/next/src/server/config.ts:71-107](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L71-L107) > [!NOTE] > Specific experimental property errors like `turbopackPersistentCaching` and `dynamicIO` dynamically rewrite their error messages to instruct developers on newer replacement APIs before forcing a fatal exit. Sources: [packages/next/src/server/config.ts:75-100](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L75-L100) ## Default Values and Config Normalization Once the user configuration file has been discovered and successfully parsed, Next.js performs baseline normalization and combines user-supplied properties with built-in default settings. This phase ensures that every configuration object consumed by downstream compilation and runtime systems contains predictable structure and fallback values even when specific properties are omitted by the developer. Sources: [packages/next/src/server/config-shared.ts:2092-2098](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2092-L2098), [packages/next/src/server/config.ts:15-20](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L15-L20) The normalization utility evaluates whether the exported configuration is a static object or a dynamic function exported by the user. Sources: [packages/next/src/server/config.ts:1915-1920](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L1915-L1920) The configuration normalization execution walk-through proceeds as follows: `loadConfig()` (or module load) → `normalizeConfig(phase, interopDefault(userConfigModule))` → checks if `typeof config === 'function'` → if functional, executes `config(phase, { defaultConfig })` passing the current phase and default settings object → awaits any returned asynchronous promise → returns the finalized configuration object. Sources: [packages/next/src/server/config-shared.ts:2092-2098](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2092-L2098) > [!NOTE] > If the user export is a function, it receives the execution `phase` string and an argument bag containing `defaultConfig`. This allows conditional configuration based on whether Next.js is running in development, production build, or export mode. Sources: [packages/next/src/server/config-shared.ts:2093-2095](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2093-L2095) The `defaultConfig` object provides fallback options for experimental properties, server options, and build structures. Specific experimental features incorporate contextual environment helpers such as `turbopackFileSystemCacheForBuildDefault()` to decide appropriate defaults based on CI environments or stable build flags. Sources: [packages/next/src/server/config-shared.ts:2066-2090](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2066-L2090) | Default Property | Baseline Value / Behavior | Purpose / Context | Sources | |------------------|---------------------------|-------------------|---------| | `globalNotFound` | `false` | Controls global 404 behavior across app routes. | [packages/next/src/server/config-shared.ts:2068-2068](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2068-L2068) | | `browserDebugInfoInTerminal` | `'warn'` | Configures logging level for browser debug information in the terminal. | [packages/next/src/server/config-shared.ts:2069-2069](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2069-L2069) | | `lockDistDir` | `true` | Locks the distribution directory during build/runtime operations. | [packages/next/src/server/config-shared.ts:2070-2070](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2070-L2070) | | `proxyClientMaxBodySize` | `10_485_760` (10MB) | Sets maximum body size allowed for proxy client requests. | [packages/next/src/server/config-shared.ts:2071-2071](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2071-L2071) | | `mcpServer` | `true` | Enables Model Context Protocol server capabilities. | [packages/next/src/server/config-shared.ts:2073-2073](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2073-L2073) | | `turbopackFileSystemCacheForDev` | `true` | Enables Turbopack file system caching during development. | [packages/next/src/server/config-shared.ts:2074-2074](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2074-L2074) | | `turbopackPluginRuntimeStrategy` | `'childProcesses'` | Sets runtime execution strategy for Turbopack plugins. | [packages/next/src/server/config-shared.ts:2077-2077](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2077-L2077) | Sources: [packages/next/src/server/config-shared.ts:2066-2081](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2066-L2081) > [!WARNING] > `turbopackFileSystemCacheForBuild` evaluates dynamically via `turbopackFileSystemCacheForBuildDefault()`. It returns `false` on stable builds unless running inside Vercel CI builder environments where remote caching is guaranteed. Sources: [packages/next/src/server/config-shared.ts:2083-2090](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts#L2083-L2090) ## Custom Routes and Headers Processing Custom routes and HTTP headers processing is orchestrated via `loadCustomRoutes()`, which concurrently evaluates user-defined asynchronous configuration functions for headers, rewrites, and redirects. Each category undergoes schema validation, prefix and locale transformation passes via `processRoutes()`, and aggregation checks to safeguard server performance. Sources: [packages/next/src/lib/load-custom-routes.ts:703-710](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L703-L710), [packages/next/src/server/config.ts:16-20](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L16-L20) When loading user-defined routing rules, execution proceeds through a strict validation and transformation pipeline: `loadCustomRoutes(config)` → executes `Promise.all([loadHeaders(config), loadRewrites(config), loadRedirects(config)])` concurrently → within each loader (e.g., `loadRedirects()`), checks if `typeof config.redirects === 'function'`, awaits the returned array, invokes `checkCustomRoutes(redirects, 'redirect')`, saves raw unedited redirects to `config._originalRedirects`, applies `processRoutes(redirects, config, 'redirect')` to inject base paths and i18n locale prefixes, re-runs `checkCustomRoutes()`, and returns the finalized rules. Sources: [packages/next/src/lib/load-custom-routes.ts:585-601](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L585-L601), [packages/next/src/lib/load-custom-routes.ts:689-710](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L689-L710) > [!WARNING] > If the combined total of custom headers, redirects, and rewrites exceeds 1000 items, `loadCustomRoutes()` emits a performance warning to the console. Sources: [packages/next/src/lib/load-custom-routes.ts:714-730](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L714-L730) Rewrites support structural categorization into three distinct execution phases, parsed from an object return value containing `beforeFiles`, `afterFiles`, and `fallback` arrays. Asset prefix rewrites are automatically prepended to `beforeFiles` unless they collide with the configured base path. Sources: [packages/next/src/lib/load-custom-routes.ts:609-630](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L609-L630), [packages/next/src/lib/load-custom-routes.ts:644-657](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L644-L657) | Route Type / Property | Sub-property / Phase | Purpose / Processing Behavior | Sources | |-----------------------|----------------------|-------------------------------|---------| | `rewrites` | `beforeFiles` | Rewrites executed before checking pages, public files, and build assets. | [packages/next/src/lib/load-custom-routes.ts:496](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L496), [packages/next/src/lib/load-custom-routes.ts:652](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L652) | | `rewrites` | `afterFiles` | Rewrites executed after checking pages, public files, and dynamic routes. | [packages/next/src/lib/load-custom-routes.ts:495](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L495), [packages/next/src/lib/load-custom-routes.ts:653](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L653) | | `rewrites` | `fallback` | Rewrites executed only after all pages and dynamic fallback routes fail to match. | [packages/next/src/lib/load-custom-routes.ts:494](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L494), [packages/next/src/lib/load-custom-routes.ts:654](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L654) | | `headers` | `onMatchHeaders` | Internal routing headers populated when deployment ID or skew cookies are active. | [packages/next/src/lib/load-custom-routes.ts:492](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L492), [packages/next/src/lib/load-custom-routes.ts:712](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L712) | Sources: [packages/next/src/lib/load-custom-routes.ts:490-499](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L490-L499), [packages/next/src/lib/load-custom-routes.ts:652-654](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L652-L654) > [!TIP] > When `config.deploymentId` and `config.experimental.useSkewCookie` are both enabled, `loadCustomRoutes()` automatically prepends a global cookie-setting header rule (`__vdpl=...`) and matches incoming requests containing the RSC header to coordinate deployment state. Sources: [packages/next/src/lib/load-custom-routes.ts:753-777](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts#L753-L777) ## Experimental Flags and Deprecation Checks Once the base configuration object is loaded and parsed, Next.js inspects its properties for deprecated options, records configured canary and experimental features, and validates Turbopack configuration compatibility. This lifecycle stage ensures that outdated configuration properties emit actionable migration warnings and that builds running under Turbopack do not inadvertently rely on unsupported Webpack-centric options. Sources: [packages/next/src/server/config.ts:112-157](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L112-L157) The deprecation inspection workflow is governed by `checkDeprecations()` and `warnOptionHasBeenDeprecated()`. When config loading completes, `checkDeprecations()` examines the raw user configuration for legacy fields. Sources: [packages/next/src/server/config.ts:139-156](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L139-L156) Adding an entry: `checkDeprecations()` → calls `warnOptionHasBeenDeprecated()` for each legacy key → splits `nestedPropertyKey` by dots to walk the configuration tree → if `found` is true, invokes `Log.warnOnce(reason)` and returns `hasWarned = true`. Sources: [packages/next/src/server/config.ts:112-137](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L112-L137) | Legacy Option Key | Replacement Option Key | Warning Reason / Message Format | Sources | |-------------------|------------------------|---------------------------------|---------| | `experimental.middlewarePrefetch` | `experimental.proxyPrefetch` | `\`experimental.middlewarePrefetch\` is deprecated. Please use \`experimental.proxyPrefetch\` instead in {configFileName}.` | [packages/next/src/server/config.ts:145-150](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L145-L150) | | `experimental.middlewareClientMaxBodySize` | `experimental.proxyClientMaxBodySize` | `\`experimental.middlewareClientMaxBodySize\` is deprecated. Please use \`experimental.proxyClientMaxBodySize\` instead in {configFileName}.` | [packages/next/src/server/config.ts:151-156](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L151-L156) | Sources: [packages/next/src/server/config.ts:145-156](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L145-L156) > [!WARNING] > If a Zod schema validation error encounters unrecognized keys under `experimental`, specific fields like `turbopackPersistentCachingForBuild` or `turbopackPersistentCaching` force an immediate fatal exit by pushing the error into `fatalErrors` and pointing developers to `turbopackFileSystemCacheForBuild` or `turbopackFileSystemCacheForDev`. Sources: [packages/next/src/server/config.ts:75-92](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts#L75-L92) When builds execute with Turbopack, `validateTurboNextConfig()` ensures that unsupported options are caught and reported. Sources: [packages/next/src/lib/turbopack-warning.ts:41-69](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L41-L69) Calling `validateTurboNextConfig({ dir, configPhase })` executes the following sequence: 1. `loadConfig(configPhase, dir, { rawConfig: true })` loads the un-normalized configuration object. 2. `flattenKeys(rawNextConfig)` recursively extracts all nested property keys from the object while skipping undefined values. 3. For each flattened key, it checks whether the key starts with any entry in `unsupportedTurbopackNextConfigOptions` and ensures its value differs from `defaultConfig`. 4. If `process.env.TURBOPACK === 'auto'`, `hasWebpackConfig` is true, and `hasTurboConfig` is false, it logs a critical error and terminates the process with `process.exit(1)`. Sources: [packages/next/src/lib/turbopack-warning.ts:41-166](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L41-L166) | Unsupported Option Path | Classification | Sources | |-------------------------|----------------|---------| | `experimental.fetchCacheKeyPrefix` | Experimental feature left to be implemented for Turbopack | [packages/next/src/lib/turbopack-warning.ts:14-14](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L14-L14) | | `experimental.clientRouterFilterAllowedRate` | Experimental feature left to be implemented | [packages/next/src/lib/turbopack-warning.ts:19-19](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L19-L19) | | `experimental.allowedRevalidateHeaderKeys` | Experimental feature unsupported by Turbopack | [packages/next/src/lib/turbopack-warning.ts:23-23](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L23-L23) | | `experimental.extensionAlias` | Experimental feature unsupported by Turbopack | [packages/next/src/lib/turbopack-warning.ts:24-24](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L24-L24) | | `experimental.fallbackNodePolyfills` | Experimental feature unsupported by Turbopack | [packages/next/src/lib/turbopack-warning.ts:25-25](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L25-L25) | | `experimental.swcTraceProfiling` | Experimental feature unsupported by Turbopack | [packages/next/src/lib/turbopack-warning.ts:27-27](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L27-L27) | | `experimental.craCompat` | Compatibility flag that might not be needed for Turbopack | [packages/next/src/lib/turbopack-warning.ts:30-30](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L30-L30) | | `experimental.disablePostcssPresetEnv` | Compatibility flag that might not be needed for Turbopack | [packages/next/src/lib/turbopack-warning.ts:31-31](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L31-L31) | | `experimental.esmExternals` | Compatibility flag that might not be needed for Turbopack | [packages/next/src/lib/turbopack-warning.ts:32-32](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L32-L32) | | `experimental.forceSwcTransforms` | Force swc-loader option unsupported by Turbopack | [packages/next/src/lib/turbopack-warning.ts:33-34](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L33-L34) | | `experimental.fullySpecified` | Compatibility flag unsupported by Turbopack | [packages/next/src/lib/turbopack-warning.ts:35-35](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L35-L35) | | `experimental.urlImports` | Compatibility flag unsupported by Turbopack | [packages/next/src/lib/turbopack-warning.ts:36-36](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L36-L36) | | `experimental.slowModuleDetection` | Compatibility flag unsupported by Turbopack | [packages/next/src/lib/turbopack-warning.ts:37-37](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L37-L37) | Sources: [packages/next/src/lib/turbopack-warning.ts:14-37](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/turbopack-warning.ts#L14-L37) ## Require Hooks and Process Initialization Next.js configures Node.js module resolution during process initialization by installing custom require hooks that route userland webpack requests to the internal bundled webpack instance. The initialization utility `loadWebpackHook()` ensures that hooks are installed exactly once per process via an `installed` boolean flag. Sources: [packages/next/src/server/config-utils.ts:1-7](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L1-L7) When invoked, `loadWebpackHook()` delegates to `addHookAliases()` from `packages/next/src/server/require-hook.ts`, populating `hookPropertyMap` with precise mappings for webpack packages, source modules, and plugins. Sources: [packages/next/src/server/config-utils.ts:8-144](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L8-L144), [packages/next/src/server/require-hook.ts:40-44](https://github.com/blade47/next.js/blob/main/packages/next/src/server/require-hook.ts#L40-L44) | Request Alias | Replacement Target | Sources | |--------------------------------------------------|-------------------------------------------------------------------|---------| | `webpack` | `next/dist/compiled/webpack/webpack-lib` | [packages/next/src/server/config-utils.ts:15-15](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L15-L15) | | `webpack/package` / `webpack/package.json` | `next/dist/compiled/webpack/package` | [packages/next/src/server/config-utils.ts:16-17](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L16-L17) | | `webpack/lib/webpack` / `webpack/lib/webpack.js` | `next/dist/compiled/webpack/webpack-lib` | [packages/next/src/server/config-utils.ts:18-19](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L18-L19) | | `webpack/lib/node/NodeEnvironmentPlugin` | `next/dist/compiled/webpack/NodeEnvironmentPlugin` | [packages/next/src/server/config-utils.ts:21-23](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L21-L23) | | `webpack-sources` | `next/dist/compiled/webpack/sources` | [packages/next/src/server/config-utils.ts:130-130](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L130-L130) | | `@babel/runtime` | `next/dist/compiled/@babel/runtime/package.json` | [packages/next/src/server/config-utils.ts:134-134](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L134-L134) | Sources: [packages/next/src/server/config-utils.ts:15-134](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-utils.ts#L15-L134) The require hook overrides `mod._resolveFilename` to intercept module resolution requests. If a requested identifier matches a key in `hookPropertyMap`, it is swapped with the resolved internal path before executing `originalResolveFilename.call()`. Sources: [packages/next/src/server/require-hook.ts:49-68](https://github.com/blade47/next.js/blob/main/packages/next/src/server/require-hook.ts#L49-L68) > [!NOTE] > `mod.prototype.require` is also patched to intercept shared runtime requests ending with `.shared-runtime`, routing them directly to the vendored contexts directory for the Pages router. Sources: [packages/next/src/server/require-hook.ts:74-86](https://github.com/blade47/next.js/blob/main/packages/next/src/server/require-hook.ts#L74-L86) ## Related - [[CLI Commands]] --- ## Technical docs: TypeScript Plugin URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/build-and-config/typescript-plugin
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/server/typescript/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/index.ts) - [packages/next/src/server/typescript/rules/config.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/config.ts) - [packages/next/src/server/typescript/rules/entry.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/entry.ts) - [packages/next/src/server/lib/router-utils/typegen.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/typegen.ts) - [packages/next/src/server/typescript/rules/client-boundary.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/client-boundary.ts) - [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts) - [packages/next/src/server/next-typescript.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-typescript.ts) - [packages/next/src/server/typescript/rules/server-boundary.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/server-boundary.ts) - [packages/next/src/server/typescript/rules/metadata.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/metadata.ts) - [packages/next/src/lib/typescript/diagnosticFormatter.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/typescript/diagnosticFormatter.ts) - [packages/next/src/server/typescript/rules/server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/typescript/diagnosticFormatter.ts) - [packages/next/src/cli/next-typegen.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-typegen.ts) - [packages/next/src/server/typescript/utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/utils.ts) - [packages/next/src/lib/verify-typescript-setup.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/verify-typescript-setup.ts) - [packages/next/src/server/typescript/rules/error.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/error.ts) - [packages/next/src/lib/load-custom-routes.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/load-custom-routes.ts) - [packages/next/src/bundles/babel/packages/plugin-syntax-typescript.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/babel/packages/plugin-syntax-typescript.js) - [packages/next/src/server/typescript/constant.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/constant.ts) - [packages/eslint-plugin-next/src/index.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/index.ts) - [packages/next/src/lib/typescript/runTypeCheck.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/typescript/runTypeCheck.ts) - [packages/next/src/lib/typescript/writeConfigurationDefaults.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/typescript/writeConfigurationDefaults.ts) - [packages/next/src/server/mcp/tools/get-compilation-issues.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-compilation-issues.ts) - [packages/next/src/bundles/babel/packages/preset-typescript.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/babel/packages/preset-typescript.js)
## Overview The Next.js TypeScript language service plugin enhances the development experience within the `app` directory by providing rich intellisense, inline documentation, and semantic diagnostics for entry points, route segment configurations, and server/client component boundaries. Operating as a decorator over the standard TypeScript language service, the plugin intercepts completion requests, quick info lookups, and semantic checks to enforce framework conventions, validate exported configurations, and detect disallowed API usages at design time. Sources: [packages/next/src/server/typescript/index.ts:1-33](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/index.ts#L1-L33) ## Language Service Plugin Architecture ### Overview The Next.js TypeScript language service plugin initializes through a standard plugin module factory function that accepts the TypeScript module instance and constructs a decorated proxy over the host's `LanguageService`. Initialization extracts user options from `tsconfig.json`, sets up root directory matching for the `app` directory, and delegates autocompletion, quick info, and diagnostic requests to underlying rule handlers and AST utilities. Sources: [packages/next/src/server/typescript/index.ts:31-57](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/index.ts#L31-L57), [packages/next/src/server/typescript/utils.ts:15-27](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/utils.ts#L15-L27) ### Plugin Initialization and LanguageService Proxying When the TypeScript server loads Next.js, it invokes `createTSPlugin`, which returns a factory yielding the proxy object. If the plugin configuration explicitly sets `enabled` to `false`, the unmodified language service is returned immediately. Otherwise, `init` compiles a regular expression matching the project's app directory (`/src/app` or `/app`), and a null-prototype proxy object delegates every method of `tsModule.LanguageService` via runtime application. ```typescript export const createTSPlugin: tsModule.server.PluginModuleFactory = ({ typescript: ts, }) => { function create(info: tsModule.server.PluginCreateInfo) { const isPluginEnabled = info.config.enabled ?? true if (!isPluginEnabled) { return info.languageService } init({ ts, info, }) const proxy: tsModule.LanguageService = Object.create(null) for (let k of Object.keys(info.languageService)) { const x = info.languageService[k as keyof tsModule.LanguageService] proxy[k] = (...args: Array<{}>) => x.apply(info.languageService, args) } // ... ``` Sources: [packages/next/src/server/typescript/index.ts:31-57](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/index.ts#L31-L57), [packages/next/src/server/typescript/utils.ts:15-27](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/utils.ts#L15-L27) > [!WARNING] > If `info.config.enabled` is explicitly set to `false`, the plugin bypasses initialization entirely and returns the raw host language service without attaching AST rule interceptors or diagnostics proxies. Sources: [packages/next/src/server/typescript/index.ts:40-44](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/index.ts#L40-L44) ### Core AST Utility Integration The plugin integrates tightly with TypeScript's compiler API through helper utilities in `utils.ts` and rule modules in `index.ts`. Key inspection functions check node positions, validate default function exports, parse file directives, and retrieve source files from the active program programmatically. | Utility Function | Parameters | Return Type | Description | | :--- | :--- | :--- | :--- | | `init` | `opts: { ts, info }` | `void` | Computes project root and compiles `appDirRegExp`. | | `getTypeChecker` | None | `tsModule.TypeChecker \| undefined` | Retrieves the type checker from the current language service program. | | `getSource` | `fileName: string` | `tsModule.SourceFile \| undefined` | Fetches the AST source file for the given file path. | | `isPositionInsideNode` | `position: number, node: tsModule.Node` | `boolean` | Checks if a caret position falls within a node's full start and width. | | `isDefaultFunctionExport` | `node: tsModule.Node` | `boolean` | Determines if a node is an `export default function` declaration. | | `isInsideApp` | `filePath: string` | `boolean` | Tests whether a file path resides inside the configured app directory. | | `isAppEntryFile` | `filePath: string` | `boolean` | Validates that a file is a route `page` or `layout` inside the app directory. | | `getEntryInfo` | `fileName: string, throwOnInvalidDirective?: boolean` | `{ client: boolean, server: boolean }` | Parses top-level `'use client'` or `'use server'` directives. | Sources: [packages/next/src/server/typescript/utils.ts:15-181](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/utils.ts#L15-L181) > [!NOTE] > `getEntryInfo` inspects leading expression statements in the AST source file. If both `"use client"` and `"use server"` directives appear in the same file, or if a directive is placed below other statements when `throwOnInvalidDirective` is true, it throws a diagnostic descriptor object that is caught and reported by semantic diagnostics. Sources: [packages/next/src/server/typescript/utils.ts:118-181](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/utils.ts#L118-L181) ### Diagnostic and Completion Interception Walkthrough When the language service requests semantic diagnostics for a file via `proxy.getSemanticDiagnostics(fileName)`, the plugin executes a structured inspection pipeline: 1. `info.languageService.getSemanticDiagnostics(fileName)` retrieves existing compiler diagnostics. 2. `getSource(fileName)` loads the file's AST; if missing, prior diagnostics are returned unchanged. 3. `getEntryInfo(fileName, true)` determines client and server entry status, pushing a `MISPLACED_ENTRY_DIRECTIVE` error if directive ordering rules are violated. 4. `isInsideApp(fileName)` checks if the file is within the route segment boundaries, executing `errorEntry.getSemanticDiagnostics()` if applicable. 5. `ts.forEachChild(source, node => { ... })` iterates top-level AST nodes, branching on `ts.isImportDeclaration`, `ts.isVariableStatement`, `isDefaultFunctionExport`, and `ts.isFunctionDeclaration` to accumulate import restrictions, configuration exports, metadata rules, and server/client boundary checks. Sources: [packages/next/src/server/typescript/index.ts:167-314](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/index.ts#L167-L314) ## Entry Point and Config Validation ### Overview The TypeScript plugin validates Next.js file conventions across pages, layouts, and error boundary components. It inspects component parameter bindings against allowed property lists, resolves dynamic parallel route slot directories from disk, validates route segment configuration exports for static analyzability, and enforces client boundary requirements on error files. Sources: [packages/next/src/server/typescript/rules/config.ts:1-694](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/config.ts#L1-L694), [packages/next/src/server/typescript/rules/entry.ts:1-164](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/entry.ts#L1-L164), [packages/next/src/server/typescript/rules/error.ts:1-37](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/error.ts#L1-L37) ### Page and Layout Prop Completions The `entry` module provides IDE auto-completion and diagnostics for component parameter bindings in `page.js` and layout files. When a user requests completions inside a component parameter binding pattern, `getCompletionsAtPosition` evaluates whether the file is a page or layout entry. For page entries, valid props are restricted to `params` and `searchParams`. For layout entries, the module scans the parent directory synchronously using `fs.readdirSync` with `withFileTypes: true` to discover parallel route slots (directories starting with `@`), stripping the `@` prefix and merging them into the allowed property list alongside `children`. Sources: [packages/next/src/server/typescript/rules/entry.ts:1-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/entry.ts#L1-L140) > [!NOTE] > Parallel route slot discovery inspects the file system synchronously relative to `path.dirname(fileName)`. Any directory beginning with `@` contributes its slot name to the allowed layout props and type definitions. Sources: [packages/next/src/server/typescript/rules/entry.ts:42-58](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/entry.ts#L42-L58) ### Route Segment Configuration Validation The `config` module inspects exported variable statements to ensure that route segment configurations comply with allowed export identifiers and carry valid, statically analyzable values. | Config Option | Supported Types / Literals | Validation Rule / Behavior | | :--- | :--- | :--- | | `dynamic` | `"auto" \| "force-dynamic" \| "error" \| "force-static"` | Must match allowed string literal options. | | `fetchCache` | `"force-no-store" \| "default-no-store" \| "default-cache" \| "force-cache"` | Validates static fetch caching behavior options. | | `runtime` | `"nodejs" \| "edge" \| "experimental-edge"` | Enforces supported server runtime environments. | | `maxDuration` | Numeric literal | Sets maximum execution time for the function. | | `instant` | `true \| object \| false` | Enables instant navigation validation (type and hover support only). | | `prefetch` | `"auto" \| "partial" \| "unstable_eager" \| "force-disabled" \| "allow-runtime"` | Controls client-side router prefetching behavior. | | `unstable_dynamicStaleTime` | Number | Validates `Number(value.replace(/_/g, '')) >= 0`; pages only, forbidden in layouts. | Sources: [packages/next/src/server/typescript/rules/config.ts:14-183](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/config.ts#L14-L183), [packages/next/src/server/typescript/rules/config.ts:577-690](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/config.ts#L577-L690) The semantic diagnostic checker `getSemanticDiagnosticsForExportVariableStatement` verifies that configuration initializers are statically analyzable. If a configuration value is a BigInt, object literal, regular expression, or an arbitrary runtime expression that cannot be statically evaluated, it pushes an `INVALID_OPTION_VALUE` error code (`NEXT_TS_ERRORS.INVALID_OPTION_VALUE`). Sources: [packages/next/src/server/typescript/rules/config.ts:577-690](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/config.ts#L577-L690) > [!WARNING] > Route segment configuration values must be statically analyzable literals. Dynamic expressions, runtime variables, and non-literal objects trigger an `INVALID_OPTION_VALUE` diagnostic. Sources: [packages/next/src/server/typescript/rules/config.ts:658-672](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/config.ts#L658-L672) ### Error Boundary Client Directive Enforcement The `errorEntry` module verifies that error boundary files (`error.tsx` or `global-error.tsx`) are marked as Client Components. When `getSemanticDiagnostics` runs on a source file matching `/[\\/]error\.tsx?$/` or `/[\\/]global-error\.tsx?$/`, it checks the `isClientEntry` boolean flag. If `isClientEntry` evaluates to `false`, the plugin emits a diagnostic error across the entire file (`start: 0`, length: `source.text.length`) with code `NEXT_TS_ERRORS.INVALID_ERROR_COMPONENT`, requiring the addition of the `"use client"` directive. Sources: [packages/next/src/server/typescript/rules/error.ts:7-33](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/error.ts#L7-L33) ## Server and Client Boundary Enforcement ### Overview The TypeScript plugin enforces structural and serializability boundaries between Server and Client Components by inspecting files marked with `"use client"` and `"use server"` directives, and by filtering disallowed React APIs from Server Component compilation layers. Sources: [packages/next/src/server/typescript/rules/client-boundary.ts:1-127](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/client-boundary.ts#L1-L127), [packages/next/src/server/typescript/rules/server-boundary.ts:1-159](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/server-boundary.ts#L1-L159), [packages/next/src/server/typescript/rules/server.ts:1-92](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/server.ts#L1-L92) ### Client Component Property Serializability Diagnostics When analyzing component entry files containing the `"use client"` directive, `clientBoundary` validates that props passed across the network boundary are serializable. It inspects variable declarations and function export parameters via `getSemanticDiagnosticsForExportVariableStatement` and `getSemanticDiagnosticsForFunctionExport`. Sources: [packages/next/src/server/typescript/rules/client-boundary.ts:7-124](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/client-boundary.ts#L7-L124) If a component prop type resolves to a function type node, method signature, constructor type node, or class declaration, the plugin determines whether it represents an allowed server action or framework-injected error boundary callback. Sources: [packages/next/src/server/typescript/rules/client-boundary.ts:51-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/client-boundary.ts#L51-L114) | Check Condition | Target Property Names / AST Nodes | Action / Diagnostic Outcome | | :--- | :--- | :--- | | Server Action check | `propName === 'action' || /.+Action$/.test(propName)` | Allowed; functions matching action naming conventions are exempt from serialization errors. | | Error Boundary check | `(isErrorFile || isGlobalErrorFile) && (propName === 'reset' || propName === 'retry')` | Allowed; framework-injected `reset` and `retry` functions in `error.tsx` or `global-error.tsx` are permitted. | | Invalid function prop | Function type node or method signature without valid naming | Emits warning code `NEXT_TS_ERRORS.INVALID_CLIENT_ENTRY_PROP` (71007) requiring serialization. | | Constructor / Class prop | Constructor type node or class declaration | Emits warning code `NEXT_TS_ERRORS.INVALID_CLIENT_ENTRY_PROP` (71007) for non-serializable class instances. | Sources: [packages/next/src/server/typescript/rules/client-boundary.ts:75-114](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/client-boundary.ts#L75-L114), [packages/next/src/server/typescript/constant.ts:8-8](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/constant.ts#L8-L8) > [!WARNING] > Component props in `"use client"` entry files must be serializable. Passing non-action functions, class instances, or constructors triggers a warning diagnostic (`INVALID_CLIENT_ENTRY_PROP`), unless the property is explicitly named as an action or handled as an error boundary retry callback. Sources: [packages/next/src/server/typescript/rules/client-boundary.ts:68-100](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/client-boundary.ts#L68-L100) ### Server Action Return Type Enforcement The `serverBoundary` module governs exports from files containing the `"use server"` directive. It inspects export declarations, variable statements, and function exports to ensure that all exported members evaluate to asynchronous functions returning a `Promise`. Sources: [packages/next/src/server/typescript/rules/server-boundary.ts:55-157](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/server-boundary.ts#L55-L157) The execution walkthrough for checking server entry return types proceeds through the following helper functions: 1. `isFunctionReturningPromise()` retrieves the TypeScript type at the target node and queries its call signatures using `typeChecker.getSignaturesOfType()`. 2. For each signature, it obtains the return type via `signature.getReturnType()`. If the return type is a union type, it iterates over each constituent type; otherwise, it passes the single return type directly to `isPromiseType()`. 3. `isPromiseType()` casts the type to a `tsModule.TypeReference`, inspects its target reference, and matches its string representation against the pattern `/^Promise(<.+>)?$/` using `typeChecker.typeToString()`. 4. If any signature fails to return a `Promise`, `isFunctionReturningPromise` returns `false`, causing the plugin to emit an error diagnostic with code `NEXT_TS_ERRORS.INVALID_SERVER_ENTRY_RETURN` (71011). Sources: [packages/next/src/server/typescript/rules/server-boundary.ts:8-53](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/server-boundary.ts#L8-L53), [packages/next/src/server/typescript/rules/server-boundary.ts:72-81](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/server-boundary.ts#L72-L81), [packages/next/src/server/typescript/constant.ts:12-12](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/constant.ts#L12-L12) > [!NOTE] > Non-async functions exported from `"use server"` files result in error code `INVALID_SERVER_ENTRY_RETURN`. The plugin requires every exported member to resolve to an asynchronous function signature returning a `Promise`. Sources: [packages/next/src/server/typescript/rules/server-boundary.ts:144-153](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/server-boundary.ts#L144-L153) ### Disallowed Server Component React APIs The `serverLayer` module restricts stateful and DOM-dependent React APIs from being imported or used within Server Components. It evaluates completion entries, definition info nodes, and import declarations against explicit deny-lists defined in constants. Sources: [packages/next/src/server/typescript/rules/server.ts:1-90](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/server.ts#L1-L90), [packages/next/src/server/typescript/constant.ts:26-50](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/constant.ts#L26-L50) The disallowed React and React DOM APIs filtered on the server layer include: - Disallowed React APIs (`DISALLOWED_SERVER_REACT_APIS`): `useState`, `useEffect`, `useEffectEvent`, `useLayoutEffect`, `useDeferredValue`, `useImperativeHandle`, `useInsertionEffect`, `useReducer`, `useRef`, `useSyncExternalStore`, `useTransition`, `Component`, `PureComponent`, `createContext`, `createFactory`, `experimental_useOptimistic`, `useOptimistic`, `useActionState`. - Disallowed React DOM APIs (`DISALLOWED_SERVER_REACT_DOM_APIS`): `useFormStatus`, `useFormState`. Sources: [packages/next/src/server/typescript/constant.ts:26-50](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/constant.ts#L26-L50) When `getSemanticDiagnosticsForImportDeclaration` encounters an import from `'react'` or `'react-dom'` containing any of these restricted identifiers in its named bindings, it generates an error diagnostic with code `NEXT_TS_ERRORS.INVALID_SERVER_API` (71001) stating that the API is not allowed in Server Components. Sources: [packages/next/src/server/typescript/rules/server.ts:36-88](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/server.ts#L36-L88), [packages/next/src/server/typescript/constant.ts:2-2](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/constant.ts#L2-L2) ## Metadata and Special Export Diagnostics ### Overview The metadata rule engine validates static and dynamic metadata export type compliance across client and server boundaries. It intercepts variable statements, function declarations, and export declarations to ensure that Next.js special exports (`metadata`, `generateMetadata`, `viewport`, and `generateViewport`) conform to required typing and environment rules, emitting error or warning diagnostics via `NEXT_TS_ERRORS.INVALID_METADATA_EXPORT` (71008). Sources: [packages/next/src/server/typescript/rules/metadata.ts:1-74](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/metadata.ts#L1-L74), [packages/next/src/server/typescript/rules/metadata.ts:75-242](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/metadata.ts#L75-L242), [packages/next/src/server/typescript/constant.ts:9-9](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/constant.ts#L9-L9) ### Client-Side Boundary Enforcement In client entry modules, exporting `metadata` or `generateMetadata` is prohibited. The `metadata.client` handler inspects both variable statements and function declarations, as well as named export clauses in export declarations, identifying forbidden names and generating error-category diagnostics. Sources: [packages/next/src/server/typescript/rules/metadata.ts:6-74](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/metadata.ts#L6-L74) > [!WARNING] > Exporting `metadata` or `generateMetadata` from a Client Component file triggers error code `INVALID_METADATA_EXPORT` (71008) with message text stating that the API is not allowed in a Client Component. Sources: [packages/next/src/server/typescript/rules/metadata.ts:15-68](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/metadata.ts#L15-L68), [packages/next/src/server/typescript/constant.ts:9-9](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/constant.ts#L9-L9) ### Server-Side Type Compliance Walkthrough For server components and layouts, the plugin checks whether `metadata` and `generateMetadata` exports possess explicit type annotations or correct return signatures. The type validation check proceeds through the following call sequence: 1. `getSemanticDiagnosticsForExportVariableStatement()` or `getSemanticDiagnosticsForExportDeclaration()` inspects the export node and extracts its underlying declaration. 2. `hasType(node)` evaluates the declaration: for function declarations, expressions, and arrow functions, it checks `node.type`. For variable declarations initialized with arrow functions or function expressions, it inspects `node.initializer.type`. 3. If `hasType()` returns `true`, type verification passes and no diagnostics are returned. 4. If missing an explicit type, the engine inspects modifier flags using `ts.SyntaxKind.AsyncKeyword` to determine if `generateMetadata` is asynchronous. 5. A warning diagnostic with code `NEXT_TS_ERRORS.INVALID_METADATA_EXPORT` is emitted, recommending `"Metadata"` for static exports and either `"Promise"` or `"Metadata"` depending on the async modifier for `generateMetadata`. Sources: [packages/next/src/server/typescript/rules/metadata.ts:75-242](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/metadata.ts#L75-L242), [packages/next/src/server/typescript/rules/metadata.ts:244-282](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/metadata.ts#L244-L282) ### Metadata Diagnostic Reference | Export Name | Node / Context | Diagnostic Category | Error Code | Message Text / Rule Condition | | :--- | :--- | :--- | :--- | :--- | | `metadata` / `generateMetadata` | Client Component (`client`) | Error | `71008` (`INVALID_METADATA_EXPORT`) | The Next.js API is not allowed in a Client Component. | | `metadata` | Server Variable / Export (`server`) | Warning | `71008` (`INVALID_METADATA_EXPORT`) | The Next.js "metadata" export should be type of "Metadata" from "next". | | `generateMetadata` | Server Function / Export (`server`) | Warning | `71008` (`INVALID_METADATA_EXPORT`) | The Next.js "generateMetadata" export should have a return type of "Metadata" or "Promise" from "next". | Sources: [packages/next/src/server/typescript/rules/metadata.ts:1-242](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/metadata.ts#L1-L242), [packages/next/src/server/typescript/constant.ts:9-9](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/constant.ts#L9-L9) ## Route and Link Type Generation ### Overview Next.js generates typed route definitions, parameter maps, slot mappings, and validator artifacts via the `next-typegen` CLI utility, dev-server integration, and typegen modules. These files validate app pages, layouts, route handlers, and pages router endpoints, ensuring compile-time safety for typed navigation and form actions. Sources: [packages/next/src/cli/next-typegen.ts:28-122](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-typegen.ts#L28-L122), [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:1167-1202](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L1167-L1202), [packages/next/src/server/lib/router-utils/typegen.ts:430-669](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/typegen.ts#L430-L669) ### CLI Type Generation Execution Walkthrough The `next-typegen` command coordinates project discovery, route analysis, and artifact generation through a specific operational sequence: 1. `getProjectDir(directory)` resolves the base project directory, verifying its existence on disk. 2. `loadConfig(PHASE_PRODUCTION_BUILD, baseDir)` loads the project configuration, after which `installBindings()` sets up SWC binaries. 3. `findPagesDir(baseDir)` locates the `app` and `pages` directories. 4. `verifyAndRunTypeScript(...)` checks TypeScript setup options, typed routes configuration, and strict route type flags. 5. `discoverRoutes(...)` scans the filesystem for `pageRoutes`, `pageApiRoutes`, `appRoutes`, `appRouteHandlers`, `layoutRoutes`, and `slots`. 6. `createRouteTypesManifest(...)` compiles route mappings, redirects, and rewrites into a structured route types manifest. 7. `writeRouteTypesManifest(...)` and `writeValidatorFile(...)` output `routes.d.ts` and `validator.ts` into the distribution types directory. 8. `writeCacheLifeTypes(...)` and `writeRootParamsTypes(...)` emit additional `cache-life.d.ts` and `root-params.d.ts` definitions. Sources: [packages/next/src/cli/next-typegen.ts:32-121](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-typegen.ts#L32-L121) ### Validator File Generation and Route Configurations `generateValidatorFile` processes sorted route paths to produce type checks for TypeScript modules. It filters out non-TypeScript files and non-page entries before constructing type assertion blocks using `__IsExpected`. Sources: [packages/next/src/server/lib/router-utils/typegen.ts:430-483](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/typegen.ts#L430-L483) | Configuration Type | Route / Source Basis | Export Signature & Parameters | Validated File Types | | :--- | :--- | :--- | :--- | | `AppPageConfig` | `routesManifest.appPagePaths` | `default`: Component or function taking `{ params: PromisearamMap[Route]> }` | `.ts`, `.tsx` (page files) | | `PagesPageConfig` | `routesManifest.pagesRouterPagePaths` | `default`: Component or function with data fetching (`getStaticProps`, `getServerSideProps`) | `.ts`, `.tsx` | | `LayoutConfig` | `routesManifest.layoutPaths` | `default`: Component or function taking `LayoutProps` | `.ts`, `.tsx` | | `RouteHandlerConfig` | `routesManifest.appRouteHandlers` | HTTP methods (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`) taking `NextRequest` and context | `.ts`, `.tsx` | | `ApiRouteConfig` | `routesManifest.pageApiRoutes` | `default`: `(req: any, res: any) => ReturnType` | `.ts`, `.tsx` | Sources: [packages/next/src/server/lib/router-utils/typegen.ts:488-606](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/typegen.ts#L488-L606) > [!CAUTION] > Only TypeScript source files (`.ts` and `.tsx`) are included in validator file generation. JavaScript files are excluded because they exhibit too many type inference limitations for strict route verification. Sources: [packages/next/src/server/lib/router-utils/typegen.ts:445-448](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/typegen.ts#L445-L448), [packages/next/src/server/lib/router-utils/typegen.ts:686-689](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/typegen.ts#L686-L689) ### Form Props and Typed Navigation Integration The type generator defines form property types supporting typed navigation routes and server action functions. The `FormProps` type accepts an `action` property that can be a typed route implementation or a form data submission handler. ```typescript type FormRestProps = Omit export type FormProps = { /** * `action` can be either a `string` or a function. * - If `action` is a string, it will be interpreted as a path or URL to navigate to when the form is submitted. * The path will be prefetched when the form becomes visible. * - If `action` is a function, it will be called when the form is submitted. See the React docs for more. */ action: __next_route_internal_types__.RouteImpl | ((formData: FormData) => void) } & FormRestProps export default function Form(props: FormProps): JSX.Element ``` Sources: [packages/next/src/server/lib/router-utils/typegen.ts:413-426](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/typegen.ts#L413-L426) ## TypeScript Environment Verification and Diagnostics ### Overview Next.js verifies the TypeScript environment by checking package dependencies, validating and writing `tsconfig.json` default options, executing type checks against the program AST, and formatting diagnostics for user display. Sources: [packages/next/src/lib/verify-typescript-setup.ts:56-84](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/verify-typescript-setup.ts#L56-L84) ### Dependency Verification and Native Compiler Handling `verifyAndRunTypeScript` initiates verification by checking if TypeScript intent exists via `getTypeScriptIntent`, then validating required packages through `hasNecessaryDependencies`. ```typescript const typescriptPackage: MissingDependency = { file: 'typescript/lib/typescript.js', pkg: 'typescript', exportsRestrict: true, } const requiredPackages: MissingDependency[] = [ typescriptPackage, { file: '@types/react/index.d.ts', pkg: '@types/react', exportsRestrict: true, }, { file: '@types/node/index.d.ts', pkg: '@types/node', exportsRestrict: true, }, ] ``` Sources: [packages/next/src/lib/verify-typescript-setup.ts:22-40](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/verify-typescript-setup.ts#L22-L40), [packages/next/src/lib/verify-typescript-setup.ts:91-105](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/verify-typescript-setup.ts#L91-L105) > [!NOTE] > If `@typescript/native-preview` is detected in the project, Next.js can bypass missing standard `typescript` package errors for compilation while retaining `@types/react` and `@types/node` for type checking. Sources: [packages/next/src/lib/verify-typescript-setup.ts:47-54](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/verify-typescript-setup.ts#L47-L54), [packages/next/src/lib/verify-typescript-setup.ts:110-124](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/verify-typescript-setup.ts#L110-L124) ### Configuration Defaults and Desired Compiler Options `writeConfigurationDefaults` inspects the user's `tsconfig.json` and injects required or suggested compiler options based on the detected TypeScript version. | Option Key | Setting Type | Target Value / Rule | Reason / Description | | :--- | :--- | :--- | :--- | | `target` | Suggested | `ES2017` | For top-level `await` support | | `lib` | Suggested | `['dom', 'dom.iterable', 'esnext']` | Standard browser and ECMAScript globals | | `allowJs` | Suggested | `true` | Permits JavaScript files in compilation | | `skipLibCheck` | Suggested | `true` | Skips type checking of declaration files | | `strict` | Suggested | `false` | Disables strict mode flags by default | | `noEmit` | Suggested | `true` | Prevents emitting compiled output files | | `incremental` | Suggested | `true` | Enables incremental compilation caching | | `module` | Required | `esnext` | Required for dynamic `import()` support | | `esModuleInterop` | Required | `true` | Requirement for SWC and Babel interop | | `moduleResolution` | Required | `bundler` or `node` | Matches modern bundler or webpack resolution | | `resolveJsonModule` | Required | `true` | Matches webpack module resolution | | `isolatedModules` | Required | `true` | Requirement for SWC and Babel file-by-file transforms | | `jsx` | Required | `react-jsx` | Next.js uses the React automatic runtime | Sources: [packages/next/src/lib/typescript/writeConfigurationDefaults.ts:53-143](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/typescript/writeConfigurationDefaults.ts#L53-L143) ### Execution Walkthrough: Type Check and Diagnostic Formatting When `shouldRunTypeCheck` is enabled, `runTypeCheck` executes the type checking pipeline through `typescript.createProgram` or `typescript.createIncrementalProgram`, emitting diagnostics and formatting errors via `getFormattedDiagnostic`. 1. `getTypeScriptConfiguration()` retrieves compiler options and file names from `tsConfigPath`. 2. `getDevTypesPath()` filters out stale `.next/dev/types` files during build mode. 3. `debugBuildPaths` filters app or pages paths if specified in build configuration. 4. `typescript.createProgram()` or `typescript.createIncrementalProgram()` instantiates the program AST with merged `getRequiredConfiguration()` options. 5. `program.emit()` and `typescript.getPreEmitDiagnostics()` collect all compile errors and warnings, filtering out test and mock files. 6. `getFormattedDiagnostic()` processes diagnostic codes (such as `2322`, `2344`, `2345`, `2559`, and `2820`) to produce contextual code frames and layout/page error descriptions. Sources: [packages/next/src/lib/typescript/diagnosticFormatter.ts:1-52](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/typescript/diagnosticFormatter.ts#L1-L52), [packages/next/src/lib/typescript/diagnosticFormatter.ts:73-290](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/typescript/diagnosticFormatter.ts#L73-L290), [packages/next/src/lib/typescript/runTypeCheck.ts:39-149](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/typescript/runTypeCheck.ts#L39-L149), [packages/next/src/lib/typescript/runTypeCheck.ts:163-176](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/typescript/runTypeCheck.ts#L163-L176) > [!WARNING] > If a project's `tsconfig.json` extends another configuration file or includes `references`, automatic plugin and option injection is bypassed, and Next.js logs a recommendation to add the `{ name: 'next' }` plugin manually. Sources: [packages/next/src/lib/typescript/writeConfigurationDefaults.ts:221-224](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/typescript/writeConfigurationDefaults.ts#L221-L224), [packages/next/src/lib/typescript/writeConfigurationDefaults.ts:345-359](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/typescript/writeConfigurationDefaults.ts#L345-L359) ## Related - [[Configuration Loading]] --- ## Technical docs: CLI Commands URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/build-and-config/cli-commands
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/bin/next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/bin/next.ts) - [packages/next/taskfile.js](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js) - [packages/next/src/cli/next-dev.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts) - [packages/next/src/cli/next-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts) - [packages/next/src/cli/internal/static-routes-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts) - [packages/next/src/cli/next-build.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-build.ts) - [package.json](https://github.com/blade47/next.js/blob/main/package.json) - [packages/next-codemod/bin/next-codemod.ts](https://github.com/blade47/next-codemod/bin/next-codemod.ts) - [packages/next/src/cli/next-export.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-export.ts) - [packages/create-next-app/index.ts](https://github.com/blade47/create-next-app/index.ts) - [packages/next/src/cli/next-start.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-start.ts) - [packages/next/src/cli/internal/upload-trace.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/upload-trace.ts) - [packages/next/src/cli/internal/query-trace.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/query-trace.ts) - [packages/next/src/server/config.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts) - [packages/next/src/cli/next-analyze.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-analyze.ts) - [packages/next/src/cli/next-telemetry.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-telemetry.ts) - [packages/next/package.json](https://github.com/blade47/next.js/blob/main/packages/next/package.json) - [packages/next/src/server/lib/app-info-log.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/app-info-log.ts) - [packages/next/types.js](https://github.com/blade47/next.js/blob/main/packages/next/types.js) - [packages/next/src/telemetry/events/version.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/events/version.ts) - [packages/next/src/server/config-schema.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-schema.ts) - [packages/next/src/server/lib/utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/utils.ts) - [packages/next/src/server/config-shared.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config-shared.ts) - [packages/next/src/cli/next-post-build.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-post-build.ts)
## Overview ### Background and Architecture The Next.js Command-Line Interface (CLI) serves as the primary orchestration and entry point for building, developing, serving, and diagnosing Next.js applications. Architected around Commander.js within `packages/next/src/bin/next.ts`, the CLI defines root commands that map user flags and positional arguments to asynchronous execution handlers. These handlers bootstrap runtime configurations, manage child process lifecycles, and interface directly with underlying compiler pipelines such as Webpack and Turbopack. Sources: [packages/next/src/bin/next.ts:137-155](https://github.com/blade47/next.js/blob/main/packages/next/src/bin/next.ts#L137-L155) ### Subsystem Interaction and Design Principles By decoupling command-line parsing from domain logic, the CLI architecture enforces strict separation between user-facing options and core server infrastructure. Commands validate project root directories, inject environment variables (such as `__NEXT_VERSION` and `NODE_OPTIONS`), and capture session-level telemetry or diagnostic profiles (`.next-profiles/`). Adjacent tooling—including `next-codemod`, `create-next-app`, and internal trace query systems—leverage this same command routing structure to offer developers a unified terminal experience across workspace boundaries. Sources: [packages/next/src/bin/next.ts:296-402](https://github.com/blade47/next.js/blob/main/packages/next/src/bin/next.ts#L296-L402) ## Root Command and Execution Dispatch ### Dispatch Architecture The Next.js binary entry point (`packages/next/src/bin/next.ts`) initializes a `NextRootCommand` instance configured with standard metadata, help formatting, and version flags. When executed, Commander evaluates `process.argv` and dispatches control to the registered command action handler. Sources: [packages/next/src/bin/next.ts:137-156](https://github.com/blade47/next.js/blob/main/packages/next/src/bin/next.ts#L137-L156) ### Command Mapping Flow ```mermaid flowchart TD A["CLI Invocation
node packages/next/dist/bin/next"] --> B["NextRootCommand
packages/next/src/bin/next.ts"] B --> C{"Command Match"} C -->|dev / (default)| D["nextDev Options
packages/next/src/cli/next-dev.ts"] C -->|build| E["nextBuild Options
packages/next/src/cli/next-build.ts"] C -->|start| F["nextStart Options
packages/next/src/cli/next-start.ts"] C -->|info| G["nextInfo Options
packages/next/src/cli/next-info.ts"] C -->|telemetry| H["nextTelemetry
packages/next/src/cli/next-telemetry.ts"] D --> I["Server Bootstrap / Child Fork"] E --> J["Compiler Pipeline (`build()`)"] F --> K["Production Server (`startServer()`)"] ``` Sources: [packages/next/src/bin/next.ts:296-400](https://github.com/blade47/next.js/blob/main/packages/next/src/bin/next.ts#L296-L400) ## Development Server (`next dev`) ### Lifecycle and Execution Mechanism The `next dev` command starts Next.js in development mode with hot-code reloading, error reporting, and watcher orchestration. Defined as the default command in `packages/next/src/bin/next.ts`, it accepts an optional `[directory]` argument and a broad set of configuration options. When `nextDev` executes, it performs bundler resolution via `parseBundlerArgs(options)`, verifies the project root via `fileExists()`, sets up CPU profiling directories, and registers signal handlers (`SIGINT`, `SIGTERM`) to flush telemetry via `eventCliSessionStopped()` and upload traces. Sources: [packages/next/src/cli/next-dev.ts:45-189](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L45-L189), [packages/next/src/cli/next-dev.ts:204-242](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L204-L242) > [!NOTE] > If the development server is restarted within `90,000` ms (`RAGE_RESTART_THRESHOLD_MS`), the CLI marks the session span attribute `'rage-restart'` as `true` for telemetry analysis. Sources: [packages/next/src/cli/next-dev.ts:81-83](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L81-L83) ### Dev Options Reference Table | Option Flag | Type / Default | Description | | :--- | :--- | :--- | | `[directory]` | `string` (cwd) | Target application directory. | | `-p, --port ort>` | `number` (3000) | Port number to start the application on (env: `PORT`). | | `-H, --hostname ` | `string` (0.0.0.0) | Hostname on which to start the application. | | `--turbo`, `--turbopack` | `boolean` | Starts development mode using Turbopack. | | `--webpack` | `boolean` | Starts development mode using Webpack. | | `--inspect [[host:]port]` | `DebugAddress \| true` | Allows inspecting server-side code. | | `--disable-source-maps` | `boolean` (false) | Disables Dev server source maps. | | `--experimental-https` | `boolean` | Starts server with HTTPS using a self-signed certificate. | | `--experimental-cpu-prof` | `boolean` | Enables CPU profiling; saves profiles to `.next-profiles/` on exit. | | `--internal-trace [level]` | `'all' \| 'overview'` | Enables Turbopack tracing (`turbo-tasks` level or overview). | Sources: [packages/next/src/bin/next.ts:297-374](https://github.com/blade47/next.js/blob/main/packages/next/src/bin/next.ts#L297-L374), [packages/next/src/cli/next-dev.ts:45-63](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts#L45-L63) ## Production Build (`next build`) ### Build Mechanism and Trade-Offs The `next build` command compiles the application for production deployment. Implemented in `packages/next/src/cli/next-build.ts`, it establishes process titles, configures signal handlers for CPU profile dumping on termination, and validates bundler compatibility. When invoked, `nextBuild` initializes environment options, validates project root existence via `existsSync(dir)`, and parses selective build path patterns when `--debug-build-paths` is provided. Sources: [packages/next/src/cli/next-build.ts:39-170](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-build.ts#L39-L170) ### Build Sequence Flow ```mermaid sequenceDiagram participant User participant NextBuild as nextBuild() participant Bundler as parseBundlerArgs() participant CoreBuild as build() User->>NextBuild: next build [options] NextBuild->>NextBuild: Set process.title & SIGINT/SIGTERM handlers NextBuild->>Bundler: parseBundlerArgs(options) alt experimentalAnalyze && !Turbopack NextBuild-->>User: Print error & exit (Incompatible bundler) end NextBuild->>CoreBuild: build(dir, experimentalAnalyze, profile, debug, ...) CoreBuild-->>NextBuild: Compilation Result / Error alt WEBPACK_ERRORS / BUILD_OPTIMIZATION_FAILED NextBuild-->>User: Print formatted error message & exit end ``` Sources: [packages/next/src/cli/next-build.ts:39-170](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-build.ts#L39-L170) ### Design Trade-Offs Table | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | **Mangling Disabled (`--no-mangling`)** | Preserves original variable names for readable stack traces during debugging. | Increases bundle size and reduces execution performance in production. | | **Selective Build Paths (`--debug-build-paths`)** | Isolates compilation to specific route patterns, speeding up iterative debugging. | Skips full application graph validation, potentially missing cross-route type or export errors. | | **Memory Debugging Mode (`--experimental-debug-memory-usage`)** | Enables hooks to trace heap allocations and identify memory leaks. | Adds tracking overhead, degrading peak build speed. | Sources: [packages/next/src/cli/next-build.ts:76-99](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-build.ts#L76-L99) ## Production Server (`next start`) ### Startup Execution Mechanism The `next start` command launches the Next.js production server for a pre-compiled application. Implemented in `packages/next/src/cli/next-start.ts`, it enforces start-time environment contracts and manages inspector attachments through strict sequencing: populating `NEXT_PRIVATE_START_TIME`, validating port reservation via `isPortIsReserved()`, opening the Node inspector via `inspector.open()`, attaching CPU profile signal handlers, and invoking `startServer()`. Sources: [packages/next/src/cli/next-start.ts:6-91](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-start.ts#L6-L91) > [!WARNING] > Running `next start` without executing `next build` first will fail because production manifests (such as `routes-manifest.json` and build artifacts) will be missing from the `.next` directory. Sources: [packages/next/src/cli/next-start.ts:42-90](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-start.ts#L42-L90) ### Startup Sequence ```mermaid sequenceDiagram participant User participant NextStart as nextStart() participant Utils as get-reserved-port participant Server as startServer() User->>NextStart: next start [options] NextStart->>NextStart: Set NEXT_PRIVATE_START_TIME NextStart->>Utils: isPortIsReserved(port) alt Port Reserved Utils-->>NextStart: True NextStart-->>User: Print explanation & exit(1) end NextStart->>NextStart: Open inspector if --inspect supplied NextStart->>Server: startServer({ dir, isDev: false, hostname, port, keepAliveTimeout }) ``` Sources: [packages/next/src/cli/next-start.ts:6-91](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-start.ts#L6-L91) ## System Diagnostics (`next info`) ### Diagnostics Collection Mechanism The `next info` command collects and outputs environment diagnostics, binary versions, relevant package versions, and Next.js configuration properties to aid in bug reporting. Implemented in `packages/next/src/cli/next-info.ts`, it gathers system stats asynchronously across operating system properties, package manager binaries, package versions, and configuration outputs. Sources: [packages/next/src/cli/next-info.ts:55-169](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L55-L169) ### Diagnostic Categories - **Operating System**: Platform, architecture, OS version, total available memory in MB, and CPU core count via Node.js `os` module. Sources: [packages/next/src/cli/next-info.ts:148-153](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L148-L153) - **Binaries & Packages**: Node version, npm, Yarn, pnpm, and installed dependencies (`next`, `eslint-config-next`, `react`, `react-dom`, `typescript`, `next-rspack`). Sources: [packages/next/src/cli/next-info.ts:136-160](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L136-L160) - **Staleness Verification**: Queries npm registry dist-tags (`-/package/next/dist-tags`) to compare installed release against latest/canary versions using `parseVersionInfo()` and `getStaleness()`. Sources: [packages/next/src/cli/next-info.ts:104-120](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts#L104-L120) ## Internal Trace & Static Route Analysis ### Static Route Analysis Mechanism The Next.js CLI includes internal diagnostic commands for analyzing static route bundle sizes (`next internal static-routes-info`) and querying active Turbopack trace servers (`next internal query-trace`). Static route analysis partitions route files into 6 disjoint categories to prevent double-counting across pages and app router outputs. Sources: [packages/next/src/cli/internal/static-routes-info.ts:2-79](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L2-L79) ### File Categories Reference Table | File Category | Description | | :--- | :--- | | `clientJs` | Client-side JavaScript bundles and chunks. | | `clientCss` | Client-side stylesheet assets. | | `clientMaps` | Client source map files. | | `serverBundled` | Server-bundled JavaScript entries. | | `serverUnbundled` | Server unbundled assets (e.g., traced `node_modules` dependencies). | | `serverMaps` | Server source map files. | Sources: [packages/next/src/cli/internal/static-routes-info.ts:61-79](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/static-routes-info.ts#L61-L79) ### Trace Server Query Client Flow The `query-trace` CLI client communicates with a running Turbopack trace server via its Model Context Protocol (MCP) endpoint (`http://127.0.0.1:{port}/mcp`). ```mermaid sequenceDiagram participant User participant CLI as queryTraceCli() participant Server as MCP Trace Server (Port 5748) User->>CLI: next internal query-trace [options] CLI->>Server: POST /mcp (JSON-RPC: tools/call, query_spans) alt Connection Refused Server-->>CLI: Error (Socket hang up / ECONNREFUSED) CLI-->>User: Print instructions to start `next internal trace ` end Server-->>CLI: HTTP 200 OK (Server-Sent Events stream) CLI->>CLI: Scan for "data: " lines & parse JSON CLI-->>User: Output formatted markdown or JSON response ``` Sources: [packages/next/src/cli/internal/query-trace.ts:21-106](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/query-trace.ts#L21-L106) ## Related - [[Configuration Loading]] - [[Telemetry and Diagnostics]] --- ## Technical docs: Telemetry and Diagnostics URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/build-and-config/telemetry-and-diagnostics
Relevant source files The following files were used as context for generating this wiki page: - [packages/next/src/cli/next-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts) - [packages/next/src/diagnostics/build-diagnostics.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/diagnostics/build-diagnostics.ts) - [packages/next/src/telemetry/storage.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/storage.ts) - [packages/next/src/lib/memory/shutdown.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/memory/shutdown.ts) - [packages/next/src/lib/memory/trace.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/memory/trace.ts) - [packages/next/src/telemetry/events/build.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/events/build.ts) - [packages/next/src/trace/report/to-telemetry.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/trace/report/to-telemetry.ts) - [packages/next/src/lib/memory/gc-observer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/memory/gc-observer.ts) - [packages/next/src/cli/next-telemetry.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-telemetry.ts) - [packages/next/src/telemetry/flush-telemetry.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/flush-telemetry.ts) - [packages/next/src/lib/memory/startup.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/memory/startup.ts) - [packages/next/src/telemetry/events/swc-load-failure.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/events/swc-load-failure.ts) - [packages/next/src/server/mcp/mcp-telemetry-tracker.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/mcp-telemetry-tracker.ts) - [packages/next/src/telemetry/detached-flush.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/detached-flush.ts) - [packages/next/src/cli/internal/upload-trace.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/upload-trace.ts) - [packages/next/src/telemetry/anonymous-meta.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/anonymous-meta.ts) - [packages/next/src/server/mcp/tools/get-page-metadata.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts) - [packages/next/src/trace/trace-uploader.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/trace/trace-uploader.ts) - [packages/next/src/server/client-component-renderer-logger.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/client-component-renderer-logger.ts) - [packages/next/src/next-devtools/userspace/app/terminal-logging-config.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/userspace/app/terminal-logging-config.ts) - [packages/next/src/cli/internal/turbo-trace-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts) - [packages/next/src/telemetry/post-telemetry-payload.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/post-telemetry-payload.ts) - [packages/next/src/trace/upload-trace.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/trace/upload-trace.ts) - [packages/next/src/telemetry/events/version.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/events/version.ts) - [packages/next/src/server/patch-error-inspect.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/patch-error-inspect.ts) - [packages/next/src/server/lib/app-info-log.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/app-info-log.ts) - [packages/next/src/next-devtools/server/get-next-error-feedback-middleware.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/server/get-next-error-feedback-middleware.ts) - [packages/next/src/cli/next-analyze.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-analyze.ts) - [packages/next/src/telemetry/events/session-stopped.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/events/session-stopped.ts) - [packages/next/src/server/lib/router-utils/instrumentation-globals.external.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/instrumentation-globals.external.ts)
## Overview Next.js incorporates a unified telemetry and diagnostics subsystem designed to collect anonymous build and development usage metrics, track process memory performance, record garbage collection statistics, and persist structured build reports. This infrastructure enables the framework maintainers to prioritize feature development, track compatibility failures across platforms (such as SWC native bindings or glibc issues), and provide developers with granular diagnostics during builds and dev server lifecycles. Sources: [packages/next/src/telemetry/storage.ts:51-83](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/storage.ts#L51-L83) The subsystem operates across several distinct operational boundaries: persistent local storage configurations managed via the `conf` package, detached background submission processes that prevent CLI hang on exit, V8 memory profiling triggers integrated with process signals (`SIGUSR2`), and trace log filters that upload selected performance spans to remote diagnostics endpoints. By decoupling event recording from synchronous network operations and gating collection behind environment variables (`NEXT_TELEMETRY_DISABLED`) or user opt-out preferences, the architecture balances comprehensive framework observability with strict user privacy guarantees. Sources: [packages/next/src/telemetry/storage.ts:135-154](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/storage.ts#L135-L154), [packages/next/src/trace/upload-trace.ts:4-58](https://github.com/blade47/next.js/blob/main/packages/next/src/trace/upload-trace.ts#L4-L58) ```mermaid flowchart TD A["CLI Command / Dev Server"] --> B{"Telemetry Enabled?"} B -- Yes --> C["Record Event / Span"] C --> D{"Ephemeral / CI / Dev?"} D -- Dev Mode --> E["Write to _events_.json"] E --> F["Spawn Detached Flush Process"] D -- Production / CI --> G["Submit Payload via postNextTelemetryPayload"] B -- No --> H["Drop Event"] ``` Sources: [packages/next/src/telemetry/storage.ts:219-253](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/storage.ts#L219-L253) --- ## Telemetry Storage and Payload Pipeline The core telemetry interface is encapsulated within the `Telemetry` class inside `packages/next/src/telemetry/storage.ts`. Upon initialization, it inspects the local execution environment to determine storage paths. Ephemeral environments such as Continuous Integration (CI) runners or Docker containers route telemetry metadata storage into the `.next/cache` directory, whereas standard environments utilize local configuration stores. Sources: [packages/next/src/telemetry/storage.ts:41-49](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/storage.ts#L41-L49) Data privacy is enforced through a one-way SHA-256 hashing mechanism that prepends a randomly generated `salt` (16 hex bytes) before hashing project identifiers. The storage layer maintains an asynchronous event queue implemented as a `Setromise>`. When events are recorded, they are tracked concurrently and can either be submitted synchronously via `postNextTelemetryPayload` with an automatic retry wrapper or flushed via detached background worker processes. Sources: [packages/next/src/telemetry/storage.ts:113-173](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/storage.ts#L113-L173) ```typescript import { Telemetry } from 'next/dist/client/components/react-dev-overlay/../../telemetry/storage' // Example initialization and recording of a custom telemetry event const telemetry = new Telemetry({ distDir: process.cwd() }) await telemetry.record({ eventName: 'NEXT_CUSTOM_EVENT', payload: { foo: 'bar' } }) ``` Sources: [packages/next/src/telemetry/storage.ts:62-82](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/storage.ts#L62-L82), [packages/next/src/telemetry/storage.ts:174-217](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/storage.ts#L174-L217) --- ## Detached Background Flushing Mechanism To prevent CLI commands or development servers from blocking process termination while waiting for network I/O, the telemetry storage engine implements a detached flush mechanism (`flushDetached`). When a development session stops or a worker exits, unsubmitted events are serialized to a process-specific temporary file (`_events_id>.json`) inside the distribution directory. Sources: [packages/next/src/telemetry/storage.ts:226-253](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/storage.ts#L226-L253) ```mermaid sequenceDiagram participant CLI as Main Process participant FS as File System participant Detached as detached-flush.ts CLI->>FS: Write unsubmitted events to _events_.json CLI->>Detached: Spawn detached process (mode, dir, eventsFile) Note over CLI: Main process exits immediately without blocking Detached->>FS: Read and parse _eventsFile Detached->>Telemetry: Instantiate Telemetry & record(events) Detached->>Telemetry: await telemetry.flush() & post payload Detached->>FS: fs.unlinkSync(eventsFile) ``` Sources: [packages/next/src/telemetry/detached-flush.ts:13-53](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/detached-flush.ts#L13-L53) > [!WARNING] > Detached flush worker processes rely on unique process IDs (`_events_id>.json`) to prevent race conditions between parent and child processes writing concurrently to the distribution directory. Sources: [packages/next/src/telemetry/storage.ts:244-245](https://github.com/blade47/next.js/blob/main/packages/next/src/telemetry/storage.ts#L244-L245) --- ## Build Diagnostics and Incremental Metrics The diagnostics subsystem (`packages/next/src/diagnostics/build-diagnostics.ts`) manages persistent JSON artifacts written to the `.next/diagnostics/` directory during compilation and static generation. These files provide post-mortem debugging data when builds fail or performance degrades. Sources: [packages/next/src/diagnostics/build-diagnostics.ts:7-27](https://github.com/blade47/next.js/blob/main/packages/next/src/diagnostics/build-diagnostics.ts#L7-L27) | Diagnostic File Name | Target Interface | Description | | :--- | :--- | :--- | | `build-diagnostics.json` | `BuildDiagnostics` | Records active build stages and merged build configuration options. | | `fetch-metrics.json` | `Record` | Captures HTTP fetch metrics collected during static page generation per app path. | | `incremental-build-diagnostics.json` | `IncrementalBuildDiagnostics` | Tracks changed/unchanged app paths, page paths, and Git SHAs during incremental builds. | | `framework.json` | `{ name: string, version: string }` | Records the exact Next.js framework version used for the build. | Sources: [packages/next/src/diagnostics/build-diagnostics.ts:13-96](https://github.com/blade47/next.js/blob/main/packages/next/src/diagnostics/build-diagnostics.ts#L13-L96) ```typescript import { updateBuildDiagnostics } from 'next/dist/diagnostics/build-diagnostics' // Updating current build stage prior to compilation steps await updateBuildDiagnostics({ buildStage: 'bundling-webpack', buildOptions: { target: 'server' } }) ``` Sources: [packages/next/src/diagnostics/build-diagnostics.ts:48-67](https://github.com/blade47/next.js/blob/main/packages/next/src/diagnostics/build-diagnostics.ts#L48-L67) --- ## Memory Tracing and Garbage Collection Monitoring When memory debugging mode is activated via startup hooks (`packages/next/src/lib/memory/startup.ts`), Next.js hooks into Node.js V8 heap statistics and performance hooks to observe memory pressure and garbage collection overhead. Sources: [packages/next/src/lib/memory/startup.ts:7-51](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/memory/startup.ts#L7-51) ```mermaid flowchart LR A["Process Startup"] --> B["Enable Memory Debugging"] B --> C["v8.setHeapSnapshotNearHeapLimit(1)"] B --> D["v8.setFlagsFromString('--detect-ineffective-gcs-near-heap-limit')"] B --> E["Start PerformanceObserver (gc)"] B --> F["Start Periodic Timer (20s)"] F --> G["Record RSS, HeapUsed, HeapMax"] G --> H{"Heap > 70% Limit?"} H -- Yes --> I["Generate .heapsnapshot file"] ``` Sources: [packages/next/src/lib/memory/startup.ts:11-32](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/memory/startup.ts#L11-32), [packages/next/src/lib/memory/trace.ts:27-76](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/memory/trace.ts#L27-L76) The garbage collection observer (`packages/next/src/lib/memory/gc-observer.ts`) uses a `PerformanceObserver` listening for `gc` entry types. Any garbage collection cycle taking longer than `LONG_RUNNING_GC_THRESHOLD_MS` (15ms) triggers a warning log. Additionally, sending a `SIGUSR2` signal to the Node.js process forces an on-demand V8 heap snapshot export. Sources: [packages/next/src/lib/memory/gc-observer.ts:5-27](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/memory/gc-observer.ts#L5-L27), [packages/next/src/lib/memory/startup.ts:22-29](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/memory/startup.ts#L22-29) > [!IMPORTANT] > Heap snapshots generated during high memory pressure will take significant time to complete and may temporarily freeze event loop execution; they are guarded by an `alreadyGeneratedHeapSnapshot` boolean flag to prevent cascading disk writes when heap utilization remains pegged above 70%. Sources: [packages/next/src/lib/memory/trace.ts:8-9](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/memory/trace.ts#L8-9), [packages/next/src/lib/memory/trace.ts:108-117](https://github.com/blade47/next.js/blob/main/packages/next/src/lib/memory/trace.ts#L108-117) --- ## Trace Uploads and Filtering Architecture Trace event collection and uploading are managed by `packages/next/src/trace/trace-uploader.ts` and `packages/next/src/trace/upload-trace.ts`. During development or build execution, trace spans are written to `.next/trace`. The uploader script reads the trace log line by line, filters events matching predefined access lists (`DEV_ALLOWED_EVENTS` or `BUILD_ALLOWED_EVENTS`), inherits parent feature flags, and posts structured payloads containing session metadata and filtered traces. Sources: [packages/next/src/trace/trace-uploader.ts:115-225](https://github.com/blade47/next.js/blob/main/packages/next/src/trace/trace-uploader.ts#L115-L225), [packages/next/src/trace/upload-trace.ts:4-58](https://github.com/blade47/next.js/blob/main/packages/next/src/trace/upload-trace.ts#L4-L58) | Access List Category | Included Trace Span Names | | :--- | :--- | | `COMMON_ALLOWED_EVENTS` | `memory-usage` | | `DEV_ALLOWED_EVENTS` | `client-hmr-latency`, `render-path`, `hot-reloader`, `webpack-invalid-client`, `webpack-invalidated-server`, `navigation-to-hydration`, `start-dev-server`, `compile-path`, `server-restart-close-to-memory-threshold` | | `BUILD_ALLOWED_EVENTS` | `next-build`, `run-turbopack`, `webpack-compilation`, `run-webpack-compiler`, `create-entrypoints`, `static-generation`, `next-export`, `run-typescript`, `run-eslint` | Sources: [packages/next/src/trace/trace-uploader.ts:10-56](https://github.com/blade47/next.js/blob/main/packages/next/src/trace/trace-uploader.ts#L10-56) --- ## Model Context Protocol (MCP) Telemetry and Metadata Tools Next.js embeds Model Context Protocol (MCP) servers and tracking infrastructure (`packages/next/src/server/mcp/mcp-telemetry-tracker.ts`) to record tool invocations during developer assistance workflows. Tool usages are accumulated in a private `Map` and mapped into telemetry feature usage events upon session completion. Sources: [packages/next/src/server/mcp/mcp-telemetry-tracker.ts:1-47](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/mcp-telemetry-tracker.ts#L1-47), [packages/next/src/server/mcp/mcp-telemetry-tracker.ts:69-83](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/mcp-telemetry-tracker.ts#L69-L83) The `get_page_metadata` MCP tool (`packages/next/src/server/mcp/tools/get-page-metadata.ts`) queries active browser sessions via HMR communication channels, retrieves segment trie data, converts them into page metadata structures, and sorts them using a strict boundary precedence ordering: Sources: [packages/next/src/server/mcp/tools/get-page-metadata.ts:19-117](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts#L19-117) 1. Layout components (`type === 'layout'`) 2. Error or loading boundaries (`type.startsWith('boundary:')`) 3. Page components (`type === 'page'`) 4. Fallback default order (`3`) Sources: [packages/next/src/server/mcp/tools/get-page-metadata.ts:194-206](https://github.com/blade47/next.js/blob/main/packages/next/src/server/mcp/tools/get-page-metadata.ts#L194-L206) ## Related - [[CLI Commands]] --- ## Technical docs: Create Next App URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/ecosystem-packages/create-next-app
Relevant source files The following files were used as context for generating this wiki page: - [packages/create-next-app/index.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts) - [packages/create-next-app/templates/index.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts) - [packages/create-next-app/create-app.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts) - [packages/next/src/bin/next.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/bin/next.ts) - [packages/next-codemod/transforms/cra-to-next.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/cra-to-next.ts) - [packages/next/src/cli/next-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-info.ts) - [packages/next-codemod/bin/upgrade.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/upgrade.ts) - [packages/next/src/cli/next-dev.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-dev.ts) - [packages/create-next-app/package.json](https://github.com/blade47/next.js/blob/main/packages/create-next-app/package.json) - [run-evals.js](https://github.com/blade47/next.js/blob/main/run-evals.js) - [packages/next-codemod/bin/agents-md.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/agents-md.ts) - [packages/create-next-app/helpers/examples.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts) - [packages/create-next-app/templates/default-tw/js/pages/index.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw/js/pages/index.js) - [packages/next-codemod/bin/next-codemod.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/next-codemod.ts) - [packages/next/src/cli/next-build.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-build.ts) - [packages/create-next-app/templates/default/js/pages/index.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/js/pages/index.js) - [packages/next/src/cli/next-test.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/next-test.ts) - [packages/create-next-app/templates/app-tw-empty/ts/next.config.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw-empty/ts/next.config.ts) - [packages/create-next-app/templates/app-tw/ts/next.config.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw/ts/next.config.ts) - [packages/create-next-app/templates/app-tw/js/app/page.js](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw/js/app/page.js) - [packages/create-next-app/tsconfig.json](https://github.com/blade47/next.js/blob/main/packages/create-next-app/tsconfig.json) - [packages/create-next-app/templates/default-tw/ts/pages/index.tsx](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw/ts/pages/index.tsx) - [packages/create-next-app/templates/app-empty/ts/next.config.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-empty/ts/next.config.ts) - [packages/create-next-app/helpers/get-pkg-manager.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/get-pkg-manager.ts) - [packages/create-next-app/helpers/install.ts](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/install.ts)
## Overview Create Next App is the official command-line bootstrapping tool designed to set up new Next.js applications quickly and reliably. It automates the entire project initialization lifecycle, eliminating manual configuration overhead by providing interactive prompts, resolving package dependencies, and scaffolding complete directory structures tailored for either the App Router or Pages Router. Sources: [packages/create-next-app/package.json:9-9](https://github.com/blade47/next.js/blob/main/packages/create-next-app/package.json#L9-L9), [packages/create-next-app/index.ts:41-123](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L41-L123), [packages/create-next-app/create-app.ts:28-264](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L28-L264) ## CLI Entry and Interactive Prompts ### Overview The `create-next-app` executable initializes as a Node.js CLI tool via `#!/usr/bin/env node`, parsing arguments with `commander` and managing persistent user preferences through `conf`. The CLI entry point configures the command-line interface, handles terminal signals (`SIGINT` and `SIGTERM`), and detects the executing package manager from environment variables or explicit flags. Sources: [packages/create-next-app/index.ts:1-26](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L1-L26), [packages/create-next-app/package.json:1-19](https://github.com/blade47/next.js/blob/main/packages/create-next-app/package.json#L1-L19) ### CLI Command Options The CLI parses positional directory arguments and options using `commander`. When options are passed, they determine project behavior such as template selection, package manager overrides, and installation flags. | Option Flag | Description / Default | | :--- | :--- | | `-v, --version` | Output the current version of create-next-app. | | `-h, --help` | Display help message. | | `--ts, --typescript` | Initialize as a TypeScript project. (default) | | `--js, --javascript` | Initialize as a JavaScript project. | | `--tailwind` | Initialize with Tailwind CSS config. (default) | | `--react-compiler` | Initialize with React Compiler enabled. | | `--eslint` | Initialize with ESLint config. | | `--biome` | Initialize with Biome config. | | `--app` | Initialize as an App Router project. | | `--src-dir` | Initialize inside a `src/` directory. | | `--rspack` | Enable Rspack as the bundler. | | `--import-alias refix/*>` | Specify import alias to use (default `@/*`). | | `--api` | Initialize a headless API using the App Router. | | `--empty` | Initialize an empty project. | | `--use-npm` | Explicitly bootstrap using npm. | | `--use-pnpm` | Explicitly bootstrap using pnpm. | | `--use-yarn` | Explicitly bootstrap using Yarn. | | `--use-bun` | Explicitly bootstrap using Bun. | | `--reset, --reset-preferences` | Reset saved preferences for create-next-app. | | `--skip-install` | Explicitly skip installing packages. | | `--yes` | Use saved preferences or defaults for unprovided options. | | `-e, --example ` | Bootstrap with an official example or public GitHub URL. | | `--example-path ath-to-example>` | Specify path to example when URL contains a slash in branch name. | | `--agents-md` | Include AGENTS.md to guide coding agents. (default) | | `--disable-git` | Skip initializing a git repository. | Sources: [packages/create-next-app/index.ts:41-113](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L41-L113) ### Package Manager Detection and Execution Flow The CLI determines which package manager invoked the process by inspecting `process.env.npm_config_user_agent`. The resolution follows a strict evaluation order across explicit flags and environment strings. ```mermaid flowchart TD A[Start: Parse CLI Options] --> B{Explicit flag used?} B -->|--use-npm| C[npm] B -->|--use-pnpm| D[pnpm] B -->|--use-yarn| E[yarn] B -->|--use-bun| F[bun] B -->|None| G["getPkgManager()"] G --> H{npm_config_user_agent starts with?} H -->|yarn| E H -->|pnpm| D H -->|bun| F H -->|Other / Empty| C ``` Sources: [packages/create-next-app/index.ts:128-137](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L128-L137), [packages/create-next-app/helpers/get-pkg-manager.ts:5-21](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/get-pkg-manager.ts#L5-L21) The package manager call-chain executes as follows: `getPkgManager()` inspects `process.env.npm_config_user_agent`, checking string prefixes for `yarn`, `pnpm`, or `bun`, and falls back to `npm`. When version inspection is required, `getPackageManagerVersion(packageManager)` checks `userAgentMatch` via regex `new RegExp(`${packageManager}/([\\d.]+[\\w.-]*)`)`, falling back to spawning `execSync(`${packageManager} --version`)`. Finally, `getPnpmMajorVersion()` calls `getPackageManagerVersion('pnpm')`, splits the version string by periods, and parses the major integer via `parseInt()`. Sources: [packages/create-next-app/helpers/get-pkg-manager.ts:5-65](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/get-pkg-manager.ts#L5-L65) > [!CAUTION] > If a user aborts an interactive prompt (`state.aborted`), the CLI explicitly writes the terminal cursor restoration escape sequence `\x1B[?25h` to `process.stdout` before exiting with status code `1`. Omitting this restoration step leaves the user's terminal cursor permanently hidden. Sources: [packages/create-next-app/index.ts:27-39](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L27-L39) ### Prompts and Configuration Defaults When interactive prompts run, `onPromptState` catches user abort events. If `--reset-preferences` is supplied, `Conf` clears saved settings and exits. Otherwise, default preferences are initialized with TypeScript enabled (`typescript: true`), linter disabled (`eslint: false`, `linter: 'eslint'`), Tailwind CSS enabled (`tailwind: true`), App Router enabled (`app: true`), `src/` directory disabled (`srcDir: false`), and import alias set to `@/*`. Sources: [packages/create-next-app/index.ts:138-156](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L138-L156), [packages/create-next-app/index.ts:232-243](https://github.com/blade47/next.js/blob/main/packages/create-next-app/index.ts#L232-L243) ## Project Creation and Flow Orchestration ### Overview Once user inputs, CLI flags, and package manager selections are validated, `createApp` orchestrates the target project creation flow. This includes verifying directory permissions, creating target folders, downloading or scaffolding templates, executing package installations, generating agent configuration files, initializing git repositories, and handling execution errors. Sources: [packages/create-next-app/create-app.ts:28-272](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L28-L272) ### Call-Chain Execution Walkthrough The project creation flow follows a strict execution path through its core orchestration functions: `createApp()` → `isWriteable()` → `mkdirSync()` → `isFolderEmpty()` → `process.chdir()` → `install()` → `generateAgentFiles()` → `tryGitInit()` 1. **`createApp()`**: Receives resolved configuration options including `appPath`, `packageManager`, template choices, and flags. Sources: [packages/create-next-app/create-app.ts:28-66](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L28-L66) 2. **`isWriteable()`**: Evaluates `dirname(root)` to verify whether the parent directory has write permissions before attempting file creation. Sources: [packages/create-next-app/create-app.ts:134-144](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L134-L144) 3. **`mkdirSync()`**: Creates the destination folder using `resolve(appPath)` with `{ recursive: true }`. Sources: [packages/create-next-app/create-app.ts:134-148](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L134-L148) 4. **`isFolderEmpty()`**: Inspects the target directory to confirm it contains no conflicting files; exits process code `1` if non-empty. Sources: [packages/create-next-app/create-app.ts:149-151](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L149-L151) 5. **`process.chdir()`**: Changes the current working directory to `root`. Sources: [packages/create-next-app/create-app.ts:160-160](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L160-L160) 6. **`install()`**: Spawns the package manager process via `cross-spawn` when installing dependencies or examples. Sources: [packages/create-next-app/create-app.ts:223-227](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L223-L227), [packages/create-next-app/helpers/install.ts:11-49](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/install.ts#L11-L49) 7. **`generateAgentFiles()`**: Conditionally runs when `agentsMd` is enabled to emit agent guidance files. Sources: [packages/create-next-app/create-app.ts:261-263](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L261-L263) 8. **`tryGitInit()`**: Initializes a git repository in `root` unless `disableGit` is explicitly set. Sources: [packages/create-next-app/create-app.ts:265-271](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L265-L271) ### Installation and Dependency Execution Dependency installation is managed by `install()`, which handles offline detection and passes explicit environment variables to the package manager spawn process. ```typescript export async function install( packageManager: PackageManager, isOnline: boolean ): Promise { const args: string[] = ['install'] if (!isOnline) { console.log( yellow('You appear to be offline.\nFalling back to the local cache.') ) args.push('--offline') } return new Promise((resolve, reject) => { const child = spawn(packageManager, args, { stdio: 'inherit', env: { ...process.env, ADBLOCK: '1', NODE_ENV: 'development', DISABLE_OPENCOLLECTIVE: '1', }, }) child.on('close', (code) => { if (code !== 0) { reject({ command: `${packageManager} ${args.join(' ')}` }) return } resolve() }) }) } ``` Sources: [packages/create-next-app/helpers/install.ts:11-50](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/install.ts#L11-L50) > [!WARNING] > When `packageManager` is set to `yarn` and the host environment is offline, `createApp` forces `isOnline` to `false` via `getOnline()`, which appends the `--offline` flag to installation arguments and instructs Yarn to fall back to its local cache. Sources: [packages/create-next-app/create-app.ts:153-154](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L153-L154), [packages/create-next-app/helpers/install.ts:17-23](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/install.ts#L17-L23) ### Orchestration Design Trade-offs | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | **Parent Directory Write Check (`isWriteable`)** | Fails fast before modifying the filesystem if permissions are lacking. | Extra asynchronous disk check prior to folder creation. | | **Forced `NODE_ENV: 'development'` during install** | Ensures package managers like pnpm do not skip required dev dependencies. | Overrides any production environment variables present in the shell. | | **Best-effort type generation (`runTypegen`)** | Prevents example setup failures if type generation errors out. | Suppresses critical type generation failures during bootstrapping. | Sources: [packages/create-next-app/create-app.ts:136-144](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L136-L144), [packages/create-next-app/create-app.ts:229-236](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L229-L236), [packages/create-next-app/helpers/install.ts:33-40](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/install.ts#L33-L40) ## Template Engine and Dependency Resolution ### Template Copying and Renaming The `installTemplate` function handles copying internal template files into the target project root directory, configuring file exclusions based on user selections for ESLint, Biome, and Tailwind CSS. Sources: [packages/create-next-app/templates/index.ts:48-95](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L48-L95) ```typescript const templatePath = path.join(__dirname, template, mode); const copySource = ["**"]; if (!eslint) copySource.push("!eslint.config.mjs"); if (!biome) copySource.push("!biome.json"); if (!tailwind) copySource.push("!postcss.config.mjs"); await copy(copySource, root, { parents: true, cwd: templatePath, rename(name) { switch (name) { case "gitignore": { return `.${name}`; } case "README-template.md": { return "README.md"; } default: { return name; } } }, }); ``` Sources: [packages/create-next-app/templates/index.ts:71-95](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L71-L95) > [!NOTE] > `README-template.md` is renamed to `README.md` during the copy operation to bypass limitations with `webpack-asset-relocator-loader` used by ncc. Sources: [packages/create-next-app/templates/index.ts:85-89](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L85-L89) ### Package Version Resolution and Package Manager Detection Package managers are identified through the `npm_config_user_agent` environment variable or by invoking `--version` via `execSync`. Sources: [packages/create-next-app/helpers/get-pkg-manager.ts:1-54](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/get-pkg-manager.ts#L1-L54) | Helper Function | Return Type | Description | | :--- | :--- | :--- | | `getPkgManager()` | `PackageManager` ('npm' \| 'pnpm' \| 'yarn' \| 'bun') | Inspects `npm_config_user_agent` to determine the active package manager, defaulting to 'npm'. | | `getPackageManagerVersion(packageManager)` | `string \| null` | Extracts the version string from user agent or spawns `ackageManager> --version`. | | `getPnpmMajorVersion()` | `number \| null` | Parses the major integer version specifically for `pnpm`. | Sources: [packages/create-next-app/helpers/get-pkg-manager.ts:5-65](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/get-pkg-manager.ts#L5-L65) During test runs, local workspace tarball paths supplied via `NEXT_TEST_PKG_PATHS` override standard package versions to ensure sibling packages install correctly. Sources: [packages/create-next-app/templates/index.ts:213-234](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/index.ts#L213-L234) ### Dependency Installation Execution The `install` function spawns the chosen package manager with explicit environment overrides to ensure reliable execution across different user environments. Sources: [packages/create-next-app/helpers/install.ts:11-49](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/install.ts#L11-L49) ```typescript export async function install( packageManager: PackageManager, isOnline: boolean ): Promise { const args: string[] = ['install'] if (!isOnline) { console.log( yellow('You appear to be offline.\nFalling back to the local cache.') ) args.push('--offline') } return new Promise((resolve, reject) => { const child = spawn(packageManager, args, { stdio: 'inherit', env: { ...process.env, ADBLOCK: '1', NODE_ENV: 'development', DISABLE_OPENCOLLECTIVE: '1', }, }) child.on('close', (code) => { if (code !== 0) { reject({ command: `${packageManager} ${args.join(' ')}` }) return } resolve() }) }) } ``` Sources: [packages/create-next-app/helpers/install.ts:11-50](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/install.ts#L11-L50) ## App Router and Tailwind Layouts ### Overview Templates configured for the App Router utilize a structured setup across TypeScript and JavaScript variants. The configuration files establish the typing foundation for Next.js projects using TypeScript definitions while leaving inline placeholders for user-defined options. Sources: [packages/create-next-app/templates/app-tw-empty/ts/next.config.ts:1-8](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw-empty/ts/next.config.ts#L1-L8), [packages/create-next-app/templates/app-tw/ts/next.config.ts:1-8](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw/ts/next.config.ts#L1-L8), [packages/create-next-app/templates/app-empty/ts/next.config.ts:1-8](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-empty/ts/next.config.ts#L1-L8) ### NextConfig TypeScript Setup The base TypeScript configuration file imported across empty and Tailwind-enabled App Router templates defines a strictly typed `NextConfig` object exported as the default module. Sources: [packages/create-next-app/templates/app-tw-empty/ts/next.config.ts:1-8](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw-empty/ts/next.config.ts#L1-L8), [packages/create-next-app/templates/app-tw/ts/next.config.ts:1-8](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw/ts/next.config.ts#L1-L8), [packages/create-next-app/templates/app-empty/ts/next.config.ts:1-8](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-empty/ts/next.config.ts#L1-L8) ```typescript import type { NextConfig } from "next"; const nextConfig: NextConfig = { /* config options here */ }; export default nextConfig; ``` Sources: [packages/create-next-app/templates/app-tw-empty/ts/next.config.ts:1-8](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw-empty/ts/next.config.ts#L1-L8) ### Tailwind CSS Page Layout Structure Tailwind-enabled templates include pre-styled page components under the App Router structure. The default component renders a flexible container layout incorporating responsive sizing, font styling, dark mode support via `dark:` modifiers, and external links to Vercel templates, learning resources, and documentation. Sources: [packages/create-next-app/templates/app-tw/js/app/page.js:1-66](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw/js/app/page.js#L1-L66) ```javascript import Image from "next/image"; export default function Home() { return (
Next.js logo

To get started, edit the page.js file.

Looking for a starting point or more instructions? Head over to{" "} Templates {" "} or the{" "} Learning {" "} center.

); } ``` Sources: [packages/create-next-app/templates/app-tw/js/app/page.js:1-66](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/app-tw/js/app/page.js#L1-L66) ## Pages Router Template Architecture ### Overview Pages Router templates provided by `create-next-app` structure initial project files under the `pages/` directory. These templates incorporate Google Fonts via `next/font/google`, CSS modules or Tailwind CSS, and metadata control using `next/head`. Sources: [packages/create-next-app/templates/default/js/pages/index.js:1-24](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/js/pages/index.js#L1-L24), [packages/create-next-app/templates/default-tw/js/pages/index.js:1-12](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw/js/pages/index.js#L1-L12) ### Font Integration and Configuration Pages Router starter files instantiate font loaders from `next/font/google` for variable typography. Both standard and Tailwind variants configure `Geist` and `Geist_Mono` with Latin subsets and CSS variable mappings. Sources: [packages/create-next-app/templates/default/js/pages/index.js:3-14](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/js/pages/index.js#L3-L14), [packages/create-next-app/templates/default-tw/js/pages/index.js:2-12](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw/js/pages/index.js#L2-L12), [packages/create-next-app/templates/default-tw/ts/pages/index.tsx:2-12](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw/ts/pages/index.tsx#L2-L12) ```javascript import { Geist, Geist_Mono } from "next/font/google"; const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], }); const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"], }); ``` Sources: [packages/create-next-app/templates/default/js/pages/index.js:3-14](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/js/pages/index.js#L3-L14) > [!NOTE] > Font variables defined through `next/font/google` are injected directly into root container class names alongside module styles or utility classes. > Sources: [packages/create-next-app/templates/default/js/pages/index.js:25-27](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/js/pages/index.js#L25-L27) ### Document Head and Metadata Standard Pages Router templates import `Head` from `next/head` inside `pages/index.js` to render document-level metadata including page title, meta description, viewport settings, and favicon links. Sources: [packages/create-next-app/templates/default/js/pages/index.js:1-24](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/js/pages/index.js#L1-L24) ```javascript Create Next App ``` Sources: [packages/create-next-app/templates/default/js/pages/index.js:19-24](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/js/pages/index.js#L19-L24) ### Template Variants Comparison The Pages Router templates provide different styling integrations across JavaScript and TypeScript flavors. | Template Variant | File Path | Styling Mechanism | Metadata Provider | | :--- | :--- | :--- | :--- | | Default JS | `packages/create-next-app/templates/default/js/pages/index.js` | CSS Modules (`@/styles/Home.module.css`) | `next/head` | | Default Tailwind JS | `packages/create-next-app/templates/default-tw/js/pages/index.js` | Tailwind CSS utility classes | Root layout wrapper | | Default Tailwind TS | `packages/create-next-app/templates/default-tw/ts/pages/index.tsx` | Tailwind CSS utility classes | Root layout wrapper | Sources: [packages/create-next-app/templates/default-tw/js/pages/index.js:1-79](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw/js/pages/index.js#L1-L79), [packages/create-next-app/templates/default/js/pages/index.js:1-88](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default/js/pages/index.js#L1-L88), [packages/create-next-app/templates/default-tw/ts/pages/index.tsx:1-79](https://github.com/blade47/next.js/blob/main/packages/create-next-app/templates/default-tw/ts/pages/index.tsx#L1-L79) ## Remote Example Streaming and Extraction ### Remote Example Streaming and Extraction `create-next-app` handles remote examples by inspecting GitHub URLs, validating repository existence via GitHub REST endpoints, fetching gzipped tarballs from `codeload.github.com`, and piping them through the `tar` extractor with path filtering. Sources: [packages/create-next-app/helpers/examples.ts:1-148](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L1-L148) ### GitHub URL Inspection and Validation When an example argument is passed, `createApp` parses it as a `URL` object. If valid, it verifies that the origin equals `https://github.com`. The helper function `getRepoInfo` splits the pathname into path segments (`[, username, name, t, _branch, ...file]`) to extract repository metadata. If `t` (such as `tree`) is missing or empty, it queries `https://api.github.com/repos/${username}/${name}` to resolve the repository's default branch. Sources: [packages/create-next-app/create-app.ts:71-96](https://github.com/blade47/next.js/blob/main/packages/create-next-app/create-app.ts#L71-L96), [packages/create-next-app/helpers/examples.ts:23-62](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L23-L62) Repository presence is validated by `hasRepo`, which performs an HTTP `HEAD` request via `isUrlOk` to check if `package.json` exists in the target repository contents URL at the specified branch reference. If the input is not a URL, `existsInRepo` checks the Vercel Next.js repository examples folder directly. Sources: [packages/create-next-app/helpers/examples.ts:14-87](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L14-L87) | Helper Function | Input Parameters | Validation Method | Purpose | | :--- | :--- | :--- | :--- | | `isUrlOk` | `url: string` | `fetch(url, { method: 'HEAD' })` | Returns `true` if HTTP status is `200`. | | `getRepoInfo` | `url: URL, examplePath?: string` | Pathname string splitting | Extracts `username`, `name`, `branch`, and `filePath`. | | `hasRepo` | `RepoInfo` object | `isUrlOk` on GitHub contents API | Confirms repository and `package.json` exist. | | `existsInRepo` | `nameOrUrl: string` | `isUrlOk` on Vercel examples path | Validates built-in Vercel repository examples. | Sources: [packages/create-next-app/helpers/examples.ts:14-87](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L14-L87) ### Tar Stream Fetching and Pipeline Extraction Once repository info is verified, `downloadAndExtractRepo` or `downloadAndExtractExample` fetches the tarball stream and extracts it into the target directory. Sources: [packages/create-next-app/helpers/examples.ts:99-147](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L99-L147) The download call chain executes through the following steps: `downloadAndExtractRepo()` → `downloadTarStream()` → `fetch()` → `Readable.fromWeb()` → `pipeline()` → `x()` (tar extractor). Sources: [packages/create-next-app/helpers/examples.ts:89-130](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L89-L130) > [!NOTE] > `downloadTarStream` validates that `res.body` is present before wrapping the Web API `ReadableStream` into a Node.js `Readable` stream using `Readable.fromWeb()`. > Sources: [packages/create-next-app/helpers/examples.ts:89-97](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L89-L97) The tar extraction filter normalizes Windows path separators to POSIX style (`posix.sep`) and dynamically determines the unpacked root directory from the first path segment. This prevents failures if a repository has been renamed on GitHub while an old name was used in the URL. Sources: [packages/create-next-app/helpers/examples.ts:111-128](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L111-L128) ### Example Extraction Workflow Implementation ```typescript export async function downloadAndExtractRepo( root: string, { username, name, branch, filePath }: RepoInfo ) { let rootPath: string | null = null await pipeline( await downloadTarStream( `https://codeload.github.com/${username}/${name}/tar.gz/${branch}` ), x({ cwd: root, strip: filePath ? filePath.split('/').length + 1 : 1, filter: (p: string) => { const posixPath = p.split(sep).join(posix.sep) if (rootPath === null) { const pathSegments = posixPath.split(posix.sep) rootPath = pathSegments.length ? pathSegments[0] : null } return posixPath.startsWith( `${rootPath}${filePath ? `/${filePath}/` : '/'}` ) }, }) ) } ``` Sources: [packages/create-next-app/helpers/examples.ts:99-130](https://github.com/blade47/next.js/blob/main/packages/create-next-app/helpers/examples.ts#L99-L130) ## Related - [[Quick Start]] --- ## Technical docs: Codemod Transformations URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/ecosystem-packages/codemod-transformations
Relevant source files The following files were used as context for generating this wiki page: - [packages/next-codemod/transforms/new-link.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/new-link.ts) - [packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts) - [packages/next-codemod/bin/transform.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/transform.ts) - [packages/next-codemod/transforms/url-to-withrouter.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/url-to-withrouter.ts) - [packages/next-codemod/transforms/next-image-experimental.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-image-experimental.ts) - [packages/next-codemod/bin/upgrade.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/upgrade.ts) - [packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts) - [packages/next-codemod/transforms/next-image-to-legacy-image.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-image-to-legacy-image.ts) - [packages/next-codemod/transforms/remove-unstable-prefix.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/remove-unstable-prefix.ts) - [packages/next-codemod/transforms/middleware-to-proxy.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/middleware-to-proxy.ts) - [packages/next-codemod/transforms/next-lint-to-eslint-cli.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-lint-to-eslint-cli.ts) - [packages/next-codemod/transforms/cra-to-next.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/cra-to-next.ts) - [packages/next-codemod/transforms/metadata-to-viewport-export.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/metadata-to-viewport-export.ts) - [packages/next-codemod/transforms/next-og-import.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-og-import.ts) - [packages/next-codemod/lib/utils.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/lib/utils.ts) - [packages/next-codemod/transforms/built-in-next-font.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/built-in-next-font.ts) - [packages/next-codemod/transforms/next-async-request-api.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-async-request-api.ts) - [packages/next-codemod/lib/cra-to-next/index-to-component.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/lib/cra-to-next/index-to-component.ts) - [packages/next-codemod/transforms/name-default-component.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/name-default-component.ts) - [packages/next-codemod/transforms/remove-experimental-ppr.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/remove-experimental-ppr.ts) - [packages/next-codemod/transforms/lib/async-request-api/index.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/lib/async-request-api/index.ts) - [packages/next-codemod/transforms/__testfixtures__/next-request-geo-ip/skip-empty-ast.input.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/__testfixtures__/next-request-geo-ip/skip-empty-ast.input.ts) - [packages/next-codemod/transforms/add-missing-react-import.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/add-missing-react-import.ts) - [packages/next-codemod/bin/next-codemod.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/next-codemod.ts) - [packages/next-codemod/transforms/app-dir-runtime-config-experimental-edge.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/app-dir-runtime-config-experimental-edge.ts) - [packages/next-codemod/transforms/__testfixtures__/next-request-geo-ip/skip-empty-ast.output.ts](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/__testfixtures__/next-request-geo-ip/skip-empty-ast.output.ts)
## Overview Codemod transformations automate the upgrade process for Next.js applications by programmatically modifying source code, configuration files, and project structures across major version boundaries. These transformations address breaking changes, API deprecations, and architectural shifts—such as adopting asynchronous request APIs, migrating bundlers, and updating component conventions—thereby reducing manual refactoring overhead and ensuring compatibility with newer Next.js releases. Sources: [packages/next-codemod/bin/transform.ts:1-240](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/transform.ts#L1-L240), [packages/next-codemod/bin/upgrade.ts:409-635](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/upgrade.ts#L409-L635) ## CLI Architecture and Upgrade Orchestration ### Overview The `@next/codemod` command-line interface provides a unified execution environment for running individual AST transformations and orchestrating automated multi-step project upgrades. Built on top of Commander, the entry point dispatches commands to specialized modules, ensuring proper positional option parsing, interactive prompts via `prompts`, and git safety validation prior to file modification. Sources: [packages/next-codemod/bin/next-codemod.ts:1-105](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/next-codemod.ts#L1-L105), [packages/next-codemod/bin/transform.ts:1-11](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/transform.ts#L1-L11) ### CLI Execution Model and Git Hygiene Before any transformation writes changes to disk, `checkGitStatus` inspects the working directory using `isGitClean.sync(process.cwd())`. If uncommitted changes exist, the execution halts and exits with code `1` unless the `--force` flag is supplied. ``` checkGitStatus(force) → isGitClean.sync(cwd) ──(clean)──► execute transform └──(dirty)─► exit(1) [or bypass if force] ``` Sources: [packages/next-codemod/bin/transform.ts:33-35](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/transform.ts#L33-L35), [packages/next-codemod/lib/utils.ts:4-32](https://github.com/blade47/next.js/blob/main/packages/next-codemod/lib/utils.ts#L4-L32) > [!CAUTION] > Running codemods without a clean git directory or without passing `--force` terminates execution immediately. If the execution environment is not recognized as a git repository, `checkGitStatus` catches the error and treats the workspace as clean. Sources: [packages/next-codemod/lib/utils.ts:10-14](https://github.com/blade47/next.js/blob/main/packages/next-codemod/lib/utils.ts#L10-L14) ### Interactive Upgrade Workflow and Options When invoking `runTransform` or `suggestCodemods`, the CLI interacts with users through prompts to select transformation targets, define file paths, and filter applicable codemods based on version differentials. | Option / Flag | Type / Default | Purpose | | :--- | :--- | :--- | | `-f, --force` | Boolean (`false`) | Bypass Git safety checks and forcibly run codemods | | `-d, --dry` | Boolean (`false`) | Dry run mode where no changes are written to files | | `-p, --print` | Boolean (`false`) | Print transformed files to stdout for debugging | | `--verbose` | Boolean (`false`) | Show detailed information about the transformation process | | `-j, --jscodeshift` | Array / String | Pass raw options directly down to the underlying `jscodeshift` runner | Sources: [packages/next-codemod/bin/next-codemod.ts:36-46](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/next-codemod.ts#L36-L46), [packages/next-codemod/bin/transform.ts:123-148](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/transform.ts#L123-L148) ### JSCodeshift Runner Dispatch Once files are expanded via `globby` and validation succeeds, `runTransform` constructs argument arrays for `jscodeshift`. Special transforms such as `cra-to-next` and `next-lint-to-eslint-cli` bypass `jscodeshift` entirely and invoke their default export functions directly with the expanded file paths and options. ```typescript const transformerPath = join(transformerDirectory, `${transformer}.js`) if (transformer === 'cra-to-next') { return require(transformerPath).default(filesExpanded, options) } if (transformer === 'next-lint-to-eslint-cli') { return require(transformerPath).default(filesExpanded, options) } ``` Sources: [packages/next-codemod/bin/transform.ts:102-120](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/transform.ts#L102-L120) For standard AST transforms, arguments configure parser options, ignore patterns, extensions, and the target transformer path before dispatching via `execa`. ```typescript let args = [] const { dry, print, runInBand, jscodeshift, verbose } = options if (dry) { args.push('--dry') } if (print) { args.push('--print') } if (runInBand) { args.push('--run-in-band') } if (verbose) { args.push('--verbose=2') } args.push('--parser=tsx') args.push('--ignore-pattern=**/node_modules/**') args.push('--ignore-pattern=**/.next/**') args.push('--extensions=tsx,ts,jsx,js') args = args.concat(['--transform', transformerPath]) if (jscodeshift) { args = args.concat(jscodeshift) } args = args.concat(filesExpanded) ``` Sources: [packages/next-codemod/bin/transform.ts:121-151](https://github.com/blade47/next.js/blob/main/packages/next-codemod/bin/transform.ts#L121-L151) ## Async Request APIs Migration ### Overview The `next-async-request-api` codemod orchestrates transformations for updating synchronous dynamic properties and request access patterns to leverage `async/await` and React's `use` hook. The top-level transform orchestrator coordinates multiple specialized sub-transforms sequentially across target source files. ```typescript export default function transform(file: FileInfo, api: API) { const transforms = [transformDynamicProps, transformDynamicAPI] return transforms.reduce((source, transformFn) => { const result = transformFn(source, api, file.path) if (!result) { return source } return result }, file.source) } ``` Sources: [packages/next-codemod/transforms/lib/async-request-api/index.ts:5-15](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/lib/async-request-api/index.ts#L5-L15) ### Dynamic Property and React Use Transformation The `transformDynamicProps` suite handles member access expressions on component props, checking whether accessed properties match target names and updating scopes accordingly. When transforming member access, `awaitMemberAccessOfProp` inspects functions for member expressions matching the prop name. ```typescript function awaitMemberAccessOfProp( propIdName: string, path: ASTPath, j: API['jscodeshift'] ) { const functionBody = findFunctionBody(path) const memberAccess = j(functionBody).find(j.MemberExpression, { object: { type: 'Identifier', name: propIdName, }, }) ... ``` Sources: [packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts:37-49](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts#L37-L49) > [!WARNING] > If a parent function scope is synchronous and distinct from the target function itself, the codemod cannot convert it to `async`. In this case, it inserts an error comment directly into the code at the member access site rather than failing silently. Sources: [packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts:74-90](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts#L74-L90) ### Re-Export Verification and Type Modification The codemod also inspects named exports and re-exports to flag components that need manual inspection when using `params` or `searchParams`. | Helper Function | Target Check | Action | | :--- | :--- | :--- | | `commentOnMatchedReExports` | `ExportNamedDeclaration` with specifiers matching `TARGET_NAMED_EXPORTS` or `default` | Inserts a warning comment if re-exported locally or imported from another module | | `modifyTypes` | `TSTypeLiteral` property signatures matching `TARGET_PROP_NAMES` | Adjusts TypeScript type annotations for asynchronous component signatures | Sources: [packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts:177-224](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts#L177-L224), [packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts:228-246](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/lib/async-request-api/next-async-dynamic-prop.ts#L228-L246) ## Core Component and Routing Transforms ### Overview The core component and routing transforms modernize legacy Next.js patterns by converting deprecated APIs, wrapping routing components, auto-naming components, and injecting missing imports. The suite includes codemods for `next/link` syntax updates, `url` to `withRouter` migration, `next/image` layout adjustments, component display naming, and automatic `React` imports. Sources: [packages/next-codemod/transforms/new-link.ts:1-123](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/new-link.ts#L1-L123), [packages/next-codemod/transforms/url-to-withrouter.ts:85-393](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/url-to-withrouter.ts#L85-L393), [packages/next-codemod/transforms/next-image-experimental.ts:258-328](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-image-experimental.ts#L258-L328), [packages/next-codemod/transforms/name-default-component.ts:23-104](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/name-default-component.ts#L23-L104), [packages/next-codemod/transforms/add-missing-react-import.ts:51-90](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/add-missing-react-import.ts#L51-L90) ### Link Modernization and Prop Lifting The `new-link` transform targets imports from `'next/link'`, stripping deprecated `legacyBehavior` and `passHref` attributes. For child anchor elements (``), it extracts anchor properties, deduplicates them against existing `` attributes, pushes unique props to the `` element, and replaces the child anchor with its inner children. ```typescript // Before: // About // After: // About ``` Sources: [packages/next-codemod/transforms/new-link.ts:14-116](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/new-link.ts#L14-L116) > [!WARNING] > If a link uses `legacyBehavior` but contains a child that cannot be resolved as an anchor tag, the codemod bails out of lifting props and injects an error block comment into the element's children. Sources: [packages/next-codemod/transforms/new-link.ts:71-86](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/new-link.ts#L71-L86) ### Routing and Image Transformations The `url-to-withrouter` transform locates default exports and variable declarations referencing `url` via `this.props.url` or parameter props, renames identifiers to `router`, inserts `withRouter` wrapping via `wrapNodeInFunction()`, and adds the `withRouter` import. ```typescript function wrapNodeInFunction(j, functionName, args) { const mappedArgs = args.map((node) => { if (node.type === 'ClassDeclaration') { node.type = 'ClassExpression' } return node }) return j.callExpression(j.identifier(functionName), mappedArgs) } ``` Sources: [packages/next-codemod/transforms/url-to-withrouter.ts:68-79](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/url-to-withrouter.ts#L68-L79) Image codemods handle bidirectional imports between `'next/image'`, `'next/legacy/image'`, and `'next/future/image'`. The experimental image transform replaces legacy `layout`, `objectFit`, and `objectPosition` props with inline styles and generated `sizes` attributes based on layout mappings. | Layout Type | Mapped Style | Mapped Sizes | | :--- | :--- | :--- | | `intrinsic` | `maxWidth: '100%', height: 'auto'` | `null` | | `responsive` | `width: '100%', height: 'auto'` | `'100vw'` | | `fill` | `null` | `'100vw'` | | `fixed` | `null` | `null` | Sources: [packages/next-codemod/transforms/next-image-experimental.ts:20-31](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-image-experimental.ts#L20-L31), [packages/next-codemod/transforms/next-image-to-legacy-image.ts:12-93](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-image-to-legacy-image.ts#L12-L93) ### Component Naming and React Import Resolution The `name-default-component` transform infers component identifiers from the file's base name, converting it to PascalCase via `camelCase()` and verifying validity with `isValidIdentifier()`. If an identifier collision occurs, it appends `'Component'` to the name. ```typescript const camelCase = (value: string): string => { const val = value.replace(/[-_\s.]+(.)?/g, (_match, chr) => chr ? chr.toUpperCase() : '' ) return val.slice(0, 1).toUpperCase() + val.slice(1) } const isValidIdentifier = (value: string): boolean => /^[a-zA-ZÀ-ÿ][0-9a-zA-ZÀ-ÿ]+$/.test(value) ``` Sources: [packages/next-codemod/transforms/name-default-component.ts:13-21](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/name-default-component.ts#L13-L21) The `add-missing-react-import` transform checks files for `React` member expression usages without a corresponding default import. If usage is detected, it either attaches a `React` default specifier to an existing `'react'` import declaration or unshifts a new `import React from 'react'` statement into the top-level program body. Sources: [packages/next-codemod/transforms/add-missing-react-import.ts:10-49](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/add-missing-react-import.ts#L10-L49), [packages/next-codemod/transforms/add-missing-react-import.ts:59-88](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/add-missing-react-import.ts#L59-L88) ## Configuration and Flag Cleanup Transforms ### Overview The configuration and flag cleanup transforms automate the evolution of Next.js projects by updating `next.config.js` settings, migrating experimental Turbopack options, converting middleware features to proxy equivalents, and stripping deprecated experimental features. These codemods inspect Abstract Syntax Trees (ASTs) via `jscodeshift` to modify static objects, configuration functions, arrow functions, and assignment expressions. Sources: [packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts:1-25](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts#L1-L25), [packages/next-codemod/transforms/middleware-to-proxy.ts:1-35](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/middleware-to-proxy.ts#L1-L35) ### Turbopack Configuration Migration The `next-experimental-turbo-to-turbopack` transform restructures legacy experimental Turbopack options inside Next.js configuration files. It locates `experimental.turbo` property blocks and migrates properties to a top-level `turbopack` object while routing performance and debugging flags to `experimental.turbopack*` namespaces. Unsupported options such as memory limits are removed entirely from both old and intermediate locations. | Legacy Property | Transformed Location & Name | Action | | :--- | :--- | :--- | | `experimental.turbo.minify` | `experimental.turbopackMinify` | Renamed to experimental namespace | | `experimental.turbo.treeShaking` | `experimental.turbopackTreeShaking` | Renamed to experimental namespace | | `experimental.turbo.sourceMaps` | `experimental.turbopackSourceMaps` | Renamed to experimental namespace | | `experimental.turbo.*` (regular) | `turbopack.*` | Moved to top-level object | | `memoryLimit` / `turbopackMemoryLimit` | *Removed* | Dropped entirely | Sources: [packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts:1-40](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts#L1-L40), [packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts:98-166](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts#L98-L166) > [!WARNING] > If an `experimental` configuration object becomes completely empty after extracting `turbo` properties and removing unsupported memory limits, the codemod deletes the entire `experimental` property from the configuration object. Sources: [packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts:143-155](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-experimental-turbo-to-turbopack.ts#L143-L155) ### Middleware and Runtime Flag Transforms The `middleware-to-proxy` transform updates configuration properties, type imports, and runtime segment configs when renaming middleware mechanisms to proxy equivalents. It maps `next/server` type imports (`NextMiddleware` to `NextProxy`, `MiddlewareConfig` to `ProxyConfig`) and rewrites config properties including `middlewarePrefetch`, `middlewareClientMaxBodySize`, `externalMiddlewareRewritesResolve`, and `skipMiddlewareUrlNormalize`. | Original Property / Import | Transformed Property / Import | Scope | | :--- | :--- | :--- | | `NextMiddleware` | `NextProxy` | `next/server` type imports & type references | | `MiddlewareConfig` | `ProxyConfig` | `next/server` type imports & type references | | `middlewarePrefetch` | `proxyPrefetch` | `experimental` config | | `middlewareClientMaxBodySize` | `proxyClientMaxBodySize` | `experimental` config | | `externalMiddlewareRewritesResolve` | `externalProxyRewritesResolve` | `experimental` config | | `skipMiddlewareUrlNormalize` | `skipProxyUrlNormalize` | Top-level config | Sources: [packages/next-codemod/transforms/middleware-to-proxy.ts:19-32](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/middleware-to-proxy.ts#L19-L32), [packages/next-codemod/transforms/middleware-to-proxy.ts:108-163](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/middleware-to-proxy.ts#L108-L163) Additional cleanups remove deprecated experimental flags. The `app-dir-runtime-config-experimental-edge` transform targets App Router page, layout, and route files, locating named `runtime` exports configured with `'experimental-edge'` and rewriting their string literal value to `'edge'`. Similarly, the `remove-experimental-ppr` transform strips `experimental_ppr` variable declarations, direct named exports, and specifiers from App Router files. Sources: [packages/next-codemod/transforms/app-dir-runtime-config-experimental-edge.ts:4-41](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/app-dir-runtime-config-experimental-edge.ts#L4-L41), [packages/next-codemod/transforms/remove-experimental-ppr.ts:4-78](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/remove-experimental-ppr.ts#L4-L78) > [!NOTE] > The `app-dir-runtime-config-experimental-edge` transform requires an exact match of a single named export containing the string literal `'experimental-edge'`; if multiple runtime exports or different values are present, the file is bypassed without modification. Sources: [packages/next-codemod/transforms/app-dir-runtime-config-experimental-edge.ts:26-36](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/app-dir-runtime-config-experimental-edge.ts#L26-L36) ## Metadata and Package Import Migrations ### Overview Metadata and package import migrations handle structural refactoring for Next.js features, updating font package namespaces, Open Graph imports, viewport configurations, and ESLint flat configuration files. These transforms normalize third-party or legacy integration paths to modern conventions using AST manipulation tools. Sources: [packages/next-codemod/transforms/next-lint-to-eslint-cli.ts:319-522](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-lint-to-eslint-cli.ts#L319-L522), [packages/next-codemod/transforms/metadata-to-viewport-export.ts:4-95](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/metadata-to-viewport-export.ts#L4-L95), [packages/next-codemod/transforms/next-og-import.ts:6-52](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-og-import.ts#L6-L52), [packages/next-codemod/transforms/built-in-next-font.ts:4-47](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/built-in-next-font.ts#L4-L47) ### Package Import and Font Migrations The `built-in-next-font` transform locates import declarations referencing legacy `@next/font` source strings and updates them to the built-in `next/font` package scope. It targets root packages, google subpaths, and local font variants. | Legacy Import Source | Modernized Import Source | Target Font Variant | | :--- | :--- | :--- | | `@next/font` | `next/font` | General font utilities | | `@next/font/google` | `next/font/google` | Google Fonts integration | | `@next/font/local` | `next/font/local` | Local font loading | Sources: [packages/next-codemod/transforms/built-in-next-font.ts:13-44](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/built-in-next-font.ts#L13-L44) Similarly, the `next-og-import` transform parses files for `next/server` import declarations containing `ImageResponse`. When found, it splits the specifiers, isolating `ImageResponse` into a dedicated import statement from `next/og` while preserving remaining specifiers under `next/server`. Sources: [packages/next-codemod/transforms/next-og-import.ts:6-49](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-og-import.ts#L6-L49) > [!NOTE] > The `next-og-import` transform splits specifiers dynamically: `ImageResponse` elements map to `next/og`, while all other identifiers remain tied to `next/server`. Sources: [packages/next-codemod/transforms/next-og-import.ts:17-47](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-og-import.ts#L17-L47) ### Metadata Viewport Extraction The `metadata-to-viewport-export` transform extracts mobile viewport configurations from the legacy `metadata` named export object and promotes them into an independent `viewport` export. It searches the `metadata` object declaration for specific properties (`viewport`, `colorScheme`, `themeColor`), extracts them, filters them out of the metadata object, and constructs a new named `viewport` constant export when modifications occur. Sources: [packages/next-codemod/transforms/metadata-to-viewport-export.ts:4-92](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/metadata-to-viewport-export.ts#L4-L92) > [!WARNING] > If a file lacks any matching `viewport`, `colorScheme`, or `themeColor` properties inside its `metadata` export, the transformer makes no modifications and returns the original source string unmodified. Sources: [packages/next-codemod/transforms/metadata-to-viewport-export.ts:20-22](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/metadata-to-viewport-export.ts#L20-L22), [packages/next-codemod/transforms/metadata-to-viewport-export.ts:69-72](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/metadata-to-viewport-export.ts#L69-L72) ### ESLint Flat Configuration Transforms The `next-lint-to-eslint-cli` transform updates ESLint flat configuration files (`eslint.config.js`) by replacing `FlatCompat` wrapper calls with direct config imports. It inspects `compat.extends(...)` and `compat.config({ extends: [...] })` constructs, mapping known string literals to direct identifier spreads. | Config String Literal | Mapped Spread Identifier | | :--- | :--- | | `next` | `...next` | | `next/core-web-vitals` | `...nextCoreWebVitals` | | `next/typescript` | `...nextTypescript` | Sources: [packages/next-codemod/transforms/next-lint-to-eslint-cli.ts:334-358](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-lint-to-eslint-cli.ts#L334-L358), [packages/next-codemod/transforms/next-lint-to-eslint-cli.ts:400-420](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-lint-to-eslint-cli.ts#L400-L420) > [!CAUTION] > Unrecognized non-Next.js string configurations encountered inside `compat.extends` arrays are preserved as wrapping `compat.extends()` or `compat.config()` calls rather than dropped. Sources: [packages/next-codemod/transforms/next-lint-to-eslint-cli.ts:344-357](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-lint-to-eslint-cli.ts#L344-L357), [packages/next-codemod/transforms/next-lint-to-eslint-cli.ts:415-431](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/next-lint-to-eslint-cli.ts#L415-L431) ## Create React App Framework Migration ### Overview The Create React App (CRA) framework migration codemod automates the end-to-end repository conversion of CRA and Vite projects into structured Next.js applications. Orchestrated by the `CraTransform` class and specialized jscodeshift transformers, the tool validates source directories, detects project frameworks, transforms custom DOM root rendering into Next.js compatible component wrappers, and scaffolds required routing and configuration files. Sources: [packages/next-codemod/transforms/cra-to-next.ts:31-83](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/cra-to-next.ts#L31-L83), [packages/next-codemod/lib/cra-to-next/index-to-component.ts:4-102](https://github.com/blade47/next.js/blob/main/packages/next-codemod/lib/cra-to-next/index-to-component.ts#L4-L102) ### Call-Chain Execution Walkthrough The project transformation sequence executes through a precise chain of validation, AST rewriting, and scaffolding steps. Invoking `CraTransform.transform()` proceeds through the following named operations: `CraTransform.transform()` → `runJscodeshift(indexTransformPath)` → `runJscodeshift(globalCssTransformPath)` → `fs.promises.mkdir()` → `this.updatePackageJson()` → `this.createNextConfig()` → `this.updateGitIgnore()` → `this.createPages()` During `index-to-component` execution, the transformer checks for default React DOM imports and `render` call expressions, validating render boundaries before exporting `NextIndexWrapper`. Sources: [packages/next-codemod/transforms/cra-to-next.ts:85-148](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/cra-to-next.ts#L85-L148), [packages/next-codemod/lib/cra-to-next/index-to-component.ts:21-84](https://github.com/blade47/next.js/blob/main/packages/next-codemod/lib/cra-to-next/index-to-component.ts#L21-L84) > [!WARNING] > If multiple ReactDOM render roots or nested render calls are detected during the index transformation, `CraTransform` immediately halts execution via `fatalMessage()` to prevent invalid component structures. Sources: [packages/next-codemod/transforms/cra-to-next.ts:102-113](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/cra-to-next.ts#L102-L113), [packages/next-codemod/lib/cra-to-next/index-to-component.ts:58-61](https://github.com/blade47/next.js/blob/main/packages/next-codemod/lib/cra-to-next/index-to-component.ts#L58-L61) ### Configuration and State Flags The `CraTransform` constructor inspects project dependencies, package managers, and configuration files to establish execution flags. | Property Name | Type | Purpose | | :--- | :--- | :--- | | `isCra` | boolean | Detects whether `react-scripts` is present in dependencies | | `isVite` | boolean | Detects whether Vite is present when CRA is absent | | `shouldUseTypeScript` | boolean | Checks for `tsconfig.json` or `.ts`/`.tsx` source files | | `installClient` | string | Resolves package manager (`yarn` or `npm`) via user agent or binary check | Sources: [packages/next-codemod/transforms/cra-to-next.ts:31-83](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/cra-to-next.ts#L31-L83), [packages/next-codemod/lib/cra-to-next/index-to-component.ts:4-7](https://github.com/blade47/next.js/blob/main/packages/next-codemod/lib/cra-to-next/index-to-component.ts#L4-L7) ## Related - [[CLI Commands]] --- ## Technical docs: Font Optimization URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/ecosystem-packages/font-optimization
Relevant source files The following files were used as context for generating this wiki page: - [packages/font/src/google/loader.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/loader.ts) - [packages/next/font/google/index.js](https://github.com/blade47/next.js/blob/main/packages/next/font/google/index.js) - [packages/next/font/local/index.js](https://github.com/blade47/next.js/blob/main/packages/next/font/local/index.js) - [packages/font/src/google/get-fallback-font-override-metrics.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/get-fallback-font-override-metrics.ts) - [packages/font/src/local/get-fallback-metrics-from-font-file.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/local/get-fallback-metrics-from-font-file.ts) - [packages/font/google/index.js](https://github.com/blade47/next.js/blob/main/packages/font/google/index.js) - [packages/font/src/local/loader.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/local/loader.ts) - [packages/font/local/index.js](https://github.com/blade47/next.js/blob/main/packages/font/local/index.js) - [packages/next/src/pages/_document.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/pages/_document.tsx) - [packages/next/src/next-devtools/dev-overlay/font/font-styles.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/dev-overlay/font/font-styles.tsx) - [packages/font/src/google/fetch-css-from-google-fonts.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/fetch-css-from-google-fonts.ts) - [packages/next/src/next-devtools/server/font/get-dev-overlay-font-middleware.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/server/font/get-dev-overlay-font-middleware.ts) - [packages/next-codemod/transforms/__testfixtures__/built-in-next-font/page.output.tsx](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/__testfixtures__/built-in-next-font/page.output.tsx) - [packages/next/font/google/target.css](https://github.com/blade47/next.js/blob/main/packages/next/font/google/target.css) - [packages/eslint-plugin-next/src/rules/no-page-custom-font.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-page-custom-font.ts) - [packages/font/src/google/fetch-font-file.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/fetch-font-file.ts) - [packages/next-codemod/transforms/__testfixtures__/built-in-next-font/page.input.tsx](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/__testfixtures__/built-in-next-font/page.input.tsx) - [packages/next/src/server/font-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/font-utils.ts) - [packages/font/src/local/pick-font-file-for-fallback-generation.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/local/pick-font-file-for-fallback-generation.ts) - [packages/eslint-plugin-next/src/rules/google-font-display.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/google-font-display.ts) - [packages/next/font/local/target.css](https://github.com/blade47/next.js/blob/main/packages/next/font/local/target.css) - [packages/font/src/local/index.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/local/index.ts) - [packages/font/src/google/validate-google-font-function-call.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/validate-google-font-function-call.ts) - [packages/font/google/target.css](https://github.com/blade47/next.js/blob/main/packages/font/google/target.css) - [packages/next/font/index.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/font/index.d.ts) - [packages/next/font/google/index.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/font/google/index.d.ts) - [packages/font/src/google/find-font-files-in-css.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/find-font-files-in-css.ts) - [packages/next/font/local/index.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/font/local/index.d.ts) - [packages/font/local/target.css](https://github.com/blade47/next.js/blob/main/packages/font/local/target.css) - [packages/font/src/google/google-fonts-metadata.ts](https://github.com/blade47/next.js/blob/main/packages/font/src/google/google-fonts-metadata.ts)
## Overview Font Optimization (`next/font`) is a built-in Next.js subsystem engineered to eliminate external network requests for web fonts at runtime while automatically preventing Cumulative Layout Shift (CLS). By default, fetching fonts from external providers like Google Fonts or self-hosting local font files introduces privacy concerns, layout jumps during font loading, and additional DNS round-trips. The `next/font` package addresses these challenges by shifting font fetching, subset extraction, and metric calculation to build time. Sources: [packages/font/src/google/loader.ts:28-194](https://github.com/blade47/next.js/blob/main/packages/font/src/google/loader.ts#L28-L194) The subsystem comprises two primary loaders: `nextFontGoogleFontLoader` for Google Fonts and `nextFontLocalFontLoader` for local font files. During compilation, these loaders download or read font binary buffers, parse metadata using `fontkit` or precalculated metrics (`capsizeFontsMetrics`), emit optimized font files into `.next/static/media`, and automatically inject sizing adjustments (`ascent-override`, `descent-override`, `line-gap-override`, and `size-adjust`) to match fallback system fonts like Arial or Times New Roman. This ensures zero layout shifts while completely self-hosting web typography. Sources: [packages/font/src/local/loader.ts:15-112](https://github.com/blade47/next.js/blob/main/packages/font/src/local/loader.ts#L15-L112) --- ## Google Fonts Loading Pipeline The Google Fonts loader (`nextFontGoogleFontLoader`) handles validation, URL construction, remote CSS fetching, binary downloading, and local asset replacement. When invoked during compilation, it validates options against static metadata (`googleFontsMetadata`), builds a Google Fonts API request URL containing selected weights, styles, and variable axes, and downloads the resulting CSS declarations. Sources: [packages/font/src/google/loader.ts:28-95](https://github.com/blade47/next.js/blob/main/packages/font/src/google/loader.ts#L28-L95) ```mermaid sequenceDiagram participant Caller as Build / Compiler participant Loader as nextFontGoogleFontLoader participant Google as Google Fonts API participant Emit as emitFontFile Caller->>Loader: Invoke with functionName & data Loader->>Loader: validateGoogleFontFunctionCall() Loader->>Google: fetchCSSFromGoogleFonts(url) Google-->>Loader: Return @font-face CSS Loader->>Loader: findFontFilesInCss() loop For each font file URL Loader->>Google: fetchFontFile(googleFontFileUrl) Google-->>Loader: Font Buffer (.woff2) Loader->>Emit: emitFontFile(buffer, ext, preload, adjustMetrics) Emit-->>Loader: selfHostedFileUrl end Loader->>Loader: Replace remote URLs with selfHostedFileUrl Loader-->>Caller: Return CSS and font configuration ``` Sources: [packages/font/src/google/loader.ts:28-162](https://github.com/blade47/next.js/blob/main/packages/font/src/google/loader.ts#L28-L162) The step-by-step control flow through the Google Fonts loader follows a strict sequence: 1. `validateGoogleFontFunctionCall()`: Validates that the requested font family exists, checks requested subsets, weights, and styles against `googleFontsMetadata`, and returns structured `FontOptions`. 2. `getFontAxes()` / `getGoogleFontsUrl()`: Computes the required font axes and builds the target request URL targeting modern user agents to guarantee `.woff2` responses. 3. `fetchCSSFromGoogleFonts()`: Fetches the CSS payload using a retry mechanism (`retry`) and in-memory caching (`cssCache`) to prevent duplicate compilation requests. 4. `findFontFilesInCss()`: Parses the CSS string line-by-line, tracking subset comment annotations (e.g., `/* latin */`) to determine whether individual font files match user-specified preload subsets. 5. `fetchFontFile()` & `emitFontFile()`: Downloads binary font buffers, caches them via `fontCache`, and emits them to `.next/static/media`. 6. URL Replacement: Replaces external `fonts.gstatic.com` URLs in the CSS string with the emitted local `selfHostedFileUrl` paths. Sources: [packages/font/src/google/loader.ts:28-162](https://github.com/blade47/next.js/blob/main/packages/font/src/google/loader.ts#L28-L162), [packages/font/src/google/fetch-css-from-google-fonts.ts:12-36](https://github.com/blade47/next.js/blob/main/packages/font/src/google/fetch-css-from-google-fonts.ts#L12-L36), [packages/font/src/google/find-font-files-in-css.ts:6-38](https://github.com/blade47/next.js/blob/main/packages/font/src/google/find-font-files-in-css.ts#L6-L38) --- ## Local Font Embedding and File Resolution The local font loader (`nextFontLocalFontLoader`) processes self-hosted font arrays specified via `next/font/local`. It resolves file paths relative to the calling module, reads binary buffers from the filesystem, extracts font metadata using `fontkit`, and dynamically constructs `@font-face` CSS declarations. Sources: [packages/font/src/local/loader.ts:15-34](https://github.com/blade47/next.js/blob/main/packages/font/src/local/loader.ts#L15-L34) ```mermaid flowchart TD A["Validate local font call"] --> B["Map over src array"] B --> C["Resolve path via resolve()"] C --> C1["fs.readFile(resolved)"] C1 --> D["emitFontFile() -> selfHostedFileUrl"] D --> E["Load metadata via fontFromBuffer()"] E --> F["Construct @font-face CSS properties"] F --> G["pickFontFileForFallbackGeneration()"] G --> H["getFallbackMetricsFromFontFile()"] H --> I["Return CSS, fallback metrics, and variables"] ``` Sources: [packages/font/src/local/loader.ts:35-112](https://github.com/blade47/next.js/blob/main/packages/font/src/local/loader.ts#L35-L112) When multiple font files are provided in the `src` array, `pickFontFileForFallbackGeneration()` determines which font file is used to calculate automatic fallback metrics. It evaluates weight distance from normal weight (`400`), prefers normal style over italic, and breaks ties by choosing the thinner font variant. Sources: [packages/font/src/local/pick-font-file-for-fallback-generation.ts:68-104](https://github.com/blade47/next.js/blob/main/packages/font/src/local/pick-font-file-for-fallback-generation.ts#L68-L104) > [!NOTE] > If `font-family` is explicitly declared in the `declarations` array of a local font call, `nextFontLocalFontLoader` respects the custom property; otherwise, it automatically assigns the generated variable name as the `font-family`. Sources: [packages/font/src/local/loader.ts:57-67](https://github.com/blade47/next.js/blob/main/packages/font/src/local/loader.ts#L57-L67) --- ## Fallback Font Override Metrics and Size Adjustment To prevent layout shift when web fonts swap in for fallback fonts, `next/font` calculates sizing adjustments (`size-adjust`, `ascent-override`, `descent-override`, and `line-gap-override`). For Google Fonts, `getFallbackFontOverrideMetrics()` invokes `calculateSizeAdjustValues()`, which executes the call chain `nextFontGoogleFontLoader` → `getFallbackFontOverrideMetrics` → `calculateSizeAdjustValues` → `formatName` to normalize the font family string, look up metrics in `capsizeFontsMetrics`, compute proportion factors, and format override percentages. For local fonts, `getFallbackMetricsFromFontFile()` uses `fontkit` to inspect glyph metrics and compute average character widths (`calcAverageWidth`). Sources: [packages/font/src/google/get-fallback-font-override-metrics.ts:13-27](https://github.com/blade47/next.js/blob/main/packages/font/src/google/get-fallback-font-override-metrics.ts#L13-L27), [packages/next/src/server/font-utils.ts:6-44](https://github.com/blade47/next.js/blob/main/packages/next/src/server/font-utils.ts#L6-L44) | Property | Description | Calculation Basis | | :--- | :--- | :--- | | `size-adjust` | Scales fallback font glyph dimensions to match primary font. | `mainFontAvgWidth / fallbackFontAvgWidth` | | `ascent-override` | Overrides fallback font ascent metric. | `ascent / (unitsPerEm * sizeAdjust)` | | `descent-override` | Overrides fallback font descent metric. | `descent / (unitsPerEm * sizeAdjust)` | | `line-gap-override` | Overrides fallback font line gap metric. | `lineGap / (unitsPerEm * sizeAdjust)` | | `fallbackFont` | Fallback system font name. | `Arial` (sans-serif) or `Times New Roman` (serif) | Sources: [packages/font/src/local/get-fallback-metrics-from-font-file.ts:73-95](https://github.com/blade47/next.js/blob/main/packages/font/src/local/get-fallback-metrics-from-font-file.ts#L73-L95), [packages/next/src/server/font-utils.ts:19-44](https://github.com/blade47/next.js/blob/main/packages/next/src/server/font-utils.ts#L19-L44) > [!WARNING] > Character average width calculation (`calcAverageWidth`) currently evaluates a fixed sample string (`aaabcdeeeefghiijklmnnoopqrrssttuvwxyz `) designed specifically for the Latin alphabet. Non-Latin alphabets may fail character coverage checks and skip automatic `size-adjust` calculations. Sources: [packages/font/src/local/get-fallback-metrics-from-font-file.ts:21-52](https://github.com/blade47/next.js/blob/main/packages/font/src/local/get-fallback-metrics-from-font-file.ts#L21-L52) --- ## Design Trade-offs | Design Choice | Benefit | Cost | | :--- | :--- | :--- | | **Build-time fetching & embedding** | Eliminates runtime external network requests and improves privacy. | Requires build-time network access or cached responses. | | **Precalculated Google Font metrics** | Avoids parsing remote font binaries during loader execution. | Requires updating metric tables when Google Fonts metadata changes. | | **In-memory CSS and file caches** | Prevents redundant duplicate fetches across client and server compilers. | Consumes heap memory during long-lived build processes. | | **Automatic size-adjust fallbacks** | Prevents Cumulative Layout Shift (CLS) during font loading. | Limited to Latin character frequency weighting tables. | Sources: [packages/font/src/google/loader.ts:13-92](https://github.com/blade47/next.js/blob/main/packages/font/src/google/loader.ts#L13-L92), [packages/font/src/google/get-fallback-font-override-metrics.ts:7-12](https://github.com/blade47/next.js/blob/main/packages/font/src/google/get-fallback-font-override-metrics.ts#L7-L12) --- ## ESLint Rules and Validation Next.js provides ESLint rules in `@next/eslint-plugin-next` to enforce font optimization best practices and prevent misconfigurations. Sources: [packages/eslint-plugin-next/src/rules/no-page-custom-font.ts:12-21](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-page-custom-font.ts#L12-L21) - `no-page-custom-font`: Prevents adding Google Fonts via manual `` tags inside custom pages or `pages/_document.js`, ensuring automatic optimization is not bypassed. - `google-font-display`: Enforces `font-display` configuration on Google Font link tags, recommending `&display=optional` and flagging unoptimized display settings like `auto`, `block`, or `fallback`. Sources: [packages/eslint-plugin-next/src/rules/no-page-custom-font.ts:152-167](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-page-custom-font.ts#L152-L167), [packages/eslint-plugin-next/src/rules/google-font-display.ts:39-50](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/google-font-display.ts#L39-L50) --- ## Usage Example The following example demonstrates how to configure and use optimized Google and local fonts in a Next.js application: ```typescript import { Inter, Oswald } from 'next/font/google' import localFont from 'next/font/local' // Configure Google Font with subsets and variable styling const inter = Inter({ subsets: ['latin'], display: 'swap', variable: '--font-inter', }) // Configure Local Font with custom fallback metrics const myFont = localFont({ src: './my-font.woff2', display: 'swap', adjustFontFallback: 'Arial', }) export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ) } ``` Sources: [packages/next-codemod/transforms/__testfixtures__/built-in-next-font/page.output.tsx:3-18](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/__testfixtures__/built-in-next-font/page.output.tsx#L3-L18), [packages/font/src/local/index.ts:9-25](https://github.com/blade47/next.js/blob/main/packages/font/src/local/index.ts#L9-L25) ## Related - [[App Server Rendering]] --- ## Technical docs: ESLint Rules URL: https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/ecosystem-packages/eslint-rules
Relevant source files The following files were used as context for generating this wiki page: - [packages/eslint-plugin-next/src/rules/no-page-custom-font.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-page-custom-font.ts) - [packages/eslint-plugin-next/src/index.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/index.ts) - [packages/eslint-plugin-next/src/rules/no-img-element.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-img-element.ts) - [packages/eslint-plugin-next/src/rules/google-font-preconnect.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/google-font-preconnect.ts) - [packages/next/errors.json](https://github.com/blade47/next.js/blob/main/packages/next/errors.json) - [packages/eslint-plugin-next/src/rules/google-font-display.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/google-font-display.ts) - [packages/eslint-plugin-next/src/rules/no-head-import-in-document.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-head-import-in-document.ts) - [packages/eslint-plugin-next/src/rules/no-title-in-document-head.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-title-in-document-head.ts) - [packages/eslint-plugin-next/src/rules/next-script-for-ga.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/next-script-for-ga.ts) - [packages/eslint-plugin-next/src/rules/no-script-component-in-head.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-script-component-in-head.ts) - [packages/next/src/pages/_document.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/pages/_document.tsx) - [packages/eslint-plugin-next/src/rules/no-head-element.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-head-element.ts) - [packages/eslint-plugin-next/src/rules/no-css-tags.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-css-tags.ts) - [packages/eslint-plugin-next/src/rules/no-before-interactive-script-outside-document.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-before-interactive-script-outside-document.ts) - [packages/next/src/server/typescript/rules/config.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/typescript/rules/config.ts) - [packages/eslint-plugin-next/src/rules/no-sync-scripts.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-sync-scripts.ts) - [packages/eslint-plugin-next/src/rules/no-unwanted-polyfillio.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-unwanted-polyfillio.ts) - [packages/eslint-config-next/src/index.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-config-next/src/index.ts) - [packages/eslint-plugin-next/src/rules/no-html-link-for-pages.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-html-link-for-pages.ts) - [packages/next/src/server/config.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/config.ts) - [packages/eslint-plugin-next/src/rules/no-document-import-in-page.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-document-import-in-page.ts) - [packages/next/src/client/legacy/image.tsx](https://github.com/blade47/next.js/blob/main/packages/next/src/client/legacy/image.tsx) - [packages/eslint-plugin-next/src/rules/no-location-assign-relative-destination.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-location-assign-relative-destination.ts) - [packages/eslint-plugin-next/src/rules/inline-script-id.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/inline-script-id.ts) - [packages/eslint-plugin-next/src/rules/no-styled-jsx-in-document.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-styled-jsx-in-document.ts) - [packages/eslint-plugin-next/src/rules/no-duplicate-head.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-duplicate-head.ts) - [packages/next-codemod/transforms/__testfixtures__/next-lint-to-eslint-cli/legacy-config/.eslintrc.json](https://github.com/blade47/next.js/blob/main/packages/next-codemod/transforms/__testfixtures__/next-lint-to-eslint-cli/legacy-config/.eslintrc.json) - [packages/next/src/shared/lib/get-img-props.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/get-img-props.ts) - [packages/font/local/index.js](https://github.com/blade47/next.js/blob/main/packages/font/local/index.js) - [packages/eslint-plugin-next/src/rules/no-typos.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-typos.ts)
## Overview ### Overview The `@next/eslint-plugin-next` package provides custom static analysis rules designed specifically for Next.js applications. It enforces performance best practices, proper layout structures, and correct API usage across fonts, images, scripts, and document structure. Operating directly on Abstract Syntax Trees (ASTs) parsed from source files, these rules bridge the gap between generic React linting and Next.js framework constraints, preventing common anti-patterns that harm Core Web Vitals, server-side rendering (SSR), and Largest Contentful Paint (LCP). Sources: [packages/eslint-plugin-next/src/index.ts:1-127](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/index.ts#L1-L127) By integrating with standard ESLint configurations (`@next/next/recommended` and `@next/next/core-web-vitals`), the plugin inspects JSX structures, import declarations, file paths, and exported module members. It addresses performance bottlenecks such as unoptimized `` tags and manual stylesheet inclusions, architectural errors like importing `next/document` inside standard pages, and reliability issues such as typos in data-fetching functions. Sources: [packages/eslint-plugin-next/src/index.ts:1-127](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/index.ts#L1-L127) --- ## Plugin Architecture and Configuration Integration The `@next/eslint-plugin-next` package exports an ESLint plugin object containing a metadata block, a map of all implemented rule modules, and pre-bundled configuration sets. The two primary rule sets exposed by the plugin are `recommendedRules` and `coreWebVitalsRules`. Sources: [packages/eslint-plugin-next/src/index.ts:1-56](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/index.ts#L1-L56) Each rule in the plugin is built using a helper utility `defineRule` and defines a `meta` property outlining its description, documentation URL, problem type, and an empty schema `[]`. Rules are registered inside the plugin object under kebab-case names. The main configuration suite combines these rules into distinct presets for modern flat configuration files (`Linter.Config`) and legacy configurations (`Linter.LegacyConfig`). Sources: [packages/eslint-plugin-next/src/index.ts:58-127](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/index.ts#L58-L127) ```mermaid flowchart TD A["eslint-config-next"] --> B["@next/eslint-plugin-next"] B --> C["recommended"] B --> D["core-web-vitals"] C --> E["Performance Warnings
(google-font-display, no-img-element, etc.)"] C --> F["Strict Errors
(inline-script-id, no-document-import-in-page, etc.)"] D --> G["Core Web Vitals Errors
(no-html-link-for-pages, no-sync-scripts)"] ``` Sources: [packages/eslint-plugin-next/src/index.ts:26-56](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/index.ts#L26-L56) --- ## Font Optimization and Loading Rules To guarantee optimal font rendering and prevent layout shifts (CLS), the plugin enforces precise structuring of Google Fonts and custom fonts via three rules: `google-font-display`, `google-font-preconnect`, and `no-page-custom-font`. Sources: [packages/eslint-plugin-next/src/rules/google-font-display.ts:1-62](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/google-font-display.ts#L1-L62) The `google-font-display` rule inspects `` JSX opening elements. If the `href` attribute starts with `https://fonts.googleapis.com/css`, it parses query parameters to verify that the `display` parameter is present and set to a recommended value rather than `auto`, `block`, or `fallback`. Similarly, `google-font-preconnect` verifies that links pointing to `https://fonts.gstatic.com` include the `rel="preconnect"` attribute. Sources: [packages/eslint-plugin-next/src/rules/google-font-display.ts:18-60](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/google-font-display.ts#L18-L60), [packages/eslint-plugin-next/src/rules/google-font-preconnect.ts:18-45](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/google-font-preconnect.ts#L18-L45) The `no-page-custom-font` rule checks files within the `pages` directory. If a custom Google Font link tag is included outside of `pages/_document.js` or outside the default document component, it reports an error warning that the font will only load for a single page or disable automatic font optimization. Sources: [packages/eslint-plugin-next/src/rules/no-page-custom-font.ts:22-170](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-page-custom-font.ts#L22-L170) > [!NOTE] > `google-font-display` recommends appending `&display=optional` to Google Font URLs to prevent flash of invisible text (FOIT) and layout shifts. Sources: [packages/eslint-plugin-next/src/rules/google-font-display.ts:39-42](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/google-font-display.ts#L39-L42) --- ## Image Performance and Element Rules The `no-img-element` rule prevents the usage of native HTML `` elements, which lack automatic sizing, modern format conversion, and responsive srcset generation, leading to slower Largest Contentful Paint (LCP) and higher bandwidth consumption. Sources: [packages/eslint-plugin-next/src/rules/no-img-element.ts:6-17](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-img-element.ts#L6-L17) The rule analyzes `JSXOpeningElement` nodes. When it encounters an `img` tag, it applies several conditional guards before reporting an infraction: checking if the file resides in the `app` directory, ignoring metadata route files, and ignoring `img` elements wrapped inside a `icture>` parent component. Sources: [packages/eslint-plugin-next/src/rules/no-img-element.ts:18-55](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-img-element.ts#L18-L55) ```typescript // Example triggering no-img-element export default function Profile() { return Profile } ``` Sources: [packages/eslint-plugin-next/src/rules/no-img-element.ts:49-52](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-img-element.ts#L49-L52) --- ## Script Strategy and Third-Party Integration Rules Script loading is strictly governed by rules preventing synchronous blocking scripts, missing script identifiers, and misconfigured third-party integrations. Sources: [packages/eslint-plugin-next/src/rules/no-sync-scripts.ts:5-15](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/rules/no-sync-scripts.ts#L5-L15) The `no-sync-scripts` rule flags any `