---
title: "Project Structure"
description: "The Next.js repository is structured as a monorepo containing standalone web applications, core core compiler and runtime packages, and specialized developer utilities. This architecture partitions..."
last_updated: "2026-10-12T06:38:56.847842+00:00"
canonical_url: "https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/orientation/project-structure"
---

<details>
<summary>Relevant source files</summary>

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

- [packages/next/src/build/turborepo-access-trace/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/turborepo-access-trace/index.ts)
- [apps/bundle-analyzer/components/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/components/index.ts)
- [apps/bundle-analyzer/app/page.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/app/page.tsx)
- [packages/next/src/build/swc/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/swc/index.ts)
- [packages/next/src/build/adapter/build-complete.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/adapter/build-complete.ts)
- [packages/third-parties/src/google/index.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/third-parties/src/google/index.tsx)
- [packages/next-routing/src/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-routing/src/index.ts)
- [packages/next/src/build/webpack/loaders/next-app-loader/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/webpack/loaders/next-app-loader/index.ts)
- [apps/bundle-analyzer/components/analyzer.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/components/analyzer.tsx)
- [packages/next/src/server/lib/app-dir-module.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/lib/app-dir-module.ts)
- [packages/next/src/server/dev/hot-reloader-turbopack.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/dev/hot-reloader-turbopack.ts)
- [packages/next/src/next-devtools/dev-overlay/components/dialog/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/dev-overlay/components/dialog/index.ts)
- [apps/bundle-analyzer/lib/analyze-data.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/lib/analyze-data.ts)
- [packages/next-codemod/lib/agents-md.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-codemod/lib/agents-md.ts)
- [packages/next/src/build/nft.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/nft.ts)
- [packages/next/src/server/base-server.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/base-server.ts)
- [packages/next/src/next-devtools/dev-overlay/components/overlay/index.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/dev-overlay/components/overlay/index.tsx)
- [packages/next/src/next-devtools/entrypoint.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/entrypoint.ts)
- [packages/next/src/experimental/testing/server/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/experimental/testing/server/index.ts)
- [packages/next/src/server/app-render/instant-validation/instant-validation.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/app-render/instant-validation/instant-validation.tsx)
- [apps/bundle-analyzer/lib/types.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/lib/types.ts)
- [packages/next/src/server/route-modules/app-route/shared-modules.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/route-modules/app-route/shared-modules.ts)
- [packages/next/src/build/analyze/graph-dump.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/analyze/graph-dump.ts)
- [sgconfig.yml](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/sgconfig.yml)
- [packages/next/src/next-devtools/dev-overlay/segment-explorer-trie.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/dev-overlay/segment-explorer-trie.ts)
- [packages/next/src/bundles/webpack/packages/package.js](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/bundles/webpack/packages/package.js)
- [apps/bundle-analyzer/lib/module-graph.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/lib/module-graph.ts)
- [packages/next/src/next-devtools/userspace/app/errors/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/userspace/app/errors/index.ts)
- [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx)
- [packages/next/src/next-devtools/dev-overlay.browser.tsx](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/dev-overlay.browser.tsx)
</details>

## Overview

The Next.js repository is structured as a monorepo containing standalone web applications, core core compiler and runtime packages, and specialized developer utilities. This architecture partitions code boundaries between the main runtime engine, build-time asset tracing and analysis subsystems, internal routing layers, and developer-facing overlays.

Sources: [apps/bundle-analyzer/app/page.tsx:1-5](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/app/page.tsx#L1-L5), [packages/next/src/build/turborepo-access-trace/index.ts:1-5](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/turborepo-access-trace/index.ts#L1-L5)

## Repository Root and Workspace Layout

### Structural Search Configuration

The repository root houses configuration infrastructure governing structural analysis and pattern matching across the [monorepo workspace](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/orientation/monorepo-workspace). Specifically, the configuration file `sgconfig.yml` defines paths for structural search rules, tests, and utility directories.

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/ast-grep/ast-grep/main/schemas/project.json

ruleDirs:
  - .config/ast-grep/rules
testConfigs:
  - testDir: .config/ast-grep/rule-tests
utilDirs:
  - .config/ast-grep/rule-utils
```
Sources: [sgconfig.yml:1-8](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/sgconfig.yml#L1-L8)

The ast-grep configuration maps custom rule definitions and verification suites to designated directory paths under the workspace root.

| Configuration Key | Target Path | Purpose |
| :--- | :--- | :--- |
| `ruleDirs` | `.config/ast-grep/rules` | Houses structural search and lint rules |
| `testConfigs[0].testDir` | `.config/ast-grep/rule-tests` | Locates test suites validating ast-grep rules |
| `utilDirs` | `.config/ast-grep/rule-utils` | Contains shared utility scripts for custom rules |

```yaml
ruleDirs:
  - .config/ast-grep/rules
testConfigs:
  - testDir: .config/ast-grep/rule-tests
utilDirs:
  - .config/ast-grep/rule-utils
```
Sources: [sgconfig.yml:3-8](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/sgconfig.yml#L3-L8)

> [!NOTE]
> The ast-grep language server schema is explicitly bound via the YAML comment header pointing to the official remote JSON schema definition.

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/ast-grep/ast-grep/main/schemas/project.json
```
Sources: [sgconfig.yml:1-1](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/sgconfig.yml#L1-L1)

## Standalone Applications in Apps

### Component Layer and UI Architecture

The `apps` workspace tree houses independent internal web utilities designed to inspect and diagnose build outputs, such as the `bundle-analyzer` application. This standalone application partitions its architecture into web components, view controllers, binary parsing engines, and layout utilities to process Next.js build module graphs and treemaps.

The [bundle analyzer](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/companion-packages/bundle-analyzer) web application exports core visualization and navigation components from its module index, including import chains, route typeaheads, treemap visualizers, and error states.

```typescript
export { ImportChain } from './import-chain'
export { RouteTypeahead } from './route-typeahead'
export { TreemapVisualizer } from './treemap-visualizer'
export { ErrorState } from './error-state'
```
Sources: [apps/bundle-analyzer/components/index.ts:1-4](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/components/index.ts#L1-L4)

The application provides distinct entrypoints for single build analysis and multi-build comparison via `SingleAnalyzer` and `CompareAnalyzer` wrapper components, which wrap controllers in boundary error handlers with custom fallback skeletons.

```tsx
export function SingleAnalyzer() {
  return (
    <AnalyzerBoundary defaultView={CompareView.Treemap}>
      <SingleAnalyzerController />
    </AnalyzerBoundary>
  )
}

function SingleAnalyzerController() {
  const model = useAnalyzerModel(false)
  return <SingleAnalyzerView model={model} />
}

export function CompareAnalyzer() {
  return (
    <AnalyzerBoundary defaultView={CompareView.Treemap}>
      <CompareAnalyzerController />
    </AnalyzerBoundary>
  )
}

function CompareAnalyzerController() {
  const model = useAnalyzerModel(true)
  return <CompareAnalyzerView model={model} />
}
```
Sources: [apps/bundle-analyzer/components/analyzer.tsx:60-84](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/components/analyzer.tsx#L60-L84)

> [!NOTE]
> The top-level hooks read URL search parameters before rendering data hooks to allow static prerendering bailout in Next.js router navigation.

```tsx
function useAnalyzerModel(compare: boolean) {
  // Read the URL before suspense data so static prerendering can bail out.
  useSearchParams()
```
Sources: [apps/bundle-analyzer/components/analyzer.tsx:106-108](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/components/analyzer.tsx#L106-L108)

### Binary Parsing and Data Models

The analysis pipeline processes low-level binary artifacts generated by Rust build analyzers using custom data readers. The `ModulesData` and `AnalyzeData` classes parse JSON header payloads followed by binary DataView buffers to resolve module paths, dependencies, dependents, and chunk parts without loading entire trees into high-level object graphs.

```typescript
export class ModulesData {
  private modulesHeader: ModulesDataHeader
  private modulesBinaryData: DataView
  private pathToModuleIndex: Map<string, ModuleIndex[]>

  constructor(modulesArrayBuffer: ArrayBuffer) {
    // Parse modules.data
    const modulesDataView = new DataView(modulesArrayBuffer)
    const modulesJsonLength = modulesDataView.getUint32(0, false)
    const modulesJsonBytes = new Uint8Array(
      modulesArrayBuffer,
      4,
      modulesJsonLength
    )
    const modulesJsonString = new TextDecoder('utf-8').decode(modulesJsonBytes)
    this.modulesHeader = JSON.parse(modulesJsonString) as ModulesDataHeader
    const modulesBinaryOffset = 4 + modulesJsonLength
    this.modulesBinaryData = new DataView(
      modulesArrayBuffer,
      modulesBinaryOffset
    )

    // Build pathToModuleIndex map
    this.pathToModuleIndex = new Map()
    for (let i = 0; i < this.modulesHeader.modules.length; i++) {
      const module = this.modulesHeader.modules[i]
      const existing = this.pathToModuleIndex.get(module.path)
      if (existing) {
        existing.push(i)
      } else {
        this.pathToModuleIndex.set(module.path, [i])
      }
    }
  }
```
Sources: [apps/bundle-analyzer/lib/analyze-data.ts:62-95](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/lib/analyze-data.ts#L62-L95)

To maintain structural integrity when cross-referencing route chunks against global build modules, the application asserts matching index fingerprints between data bundles.

```typescript
export function assertMatchingModuleIndex(
  modules: ModulesData,
  route: AnalyzeData,
  side: string
): void {
  const moduleHash = modules.moduleIndexHash()
  const routeHash = route.moduleIndexHash()
  if (moduleHash !== routeHash) {
    throw new Error(`${side}: analyzer module-index fingerprint mismatch`)
  }
}
```
Sources: [apps/bundle-analyzer/lib/analyze-data.ts:216-226](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/lib/analyze-data.ts#L216-L226)

| Data Structure / Class | Responsibilities | Key Methods |
| :--- | :--- | :--- |
| `ModulesData` | Parses global module graphs, dependency edges, and path lookups | `module()`, `moduleDependents()`, `moduleDependencies()` |
| `AnalyzeData` | Parses route-specific chunk parts, output files, and source trees | `source()`, `chunkPart()`, `outputFile()`, `sourceRoots()` |
| `FileNode` | Represents individual files in the treemap model | Properties: `id`, `name`, `size`, `outputType`, `server`, `client` |
| `DirectoryNode` | Represents container directories grouping child nodes | Properties: `id`, `name`, `type`, `children`, `size` |

```typescript
export class ModulesData {
  private modulesHeader: ModulesDataHeader
  private modulesBinaryData: DataView
  private pathToModuleIndex: Map<string, ModuleIndex[]>

  constructor(modulesArrayBuffer: ArrayBuffer) {
    // Parse modules.data
    const modulesDataView = new DataView(modulesArrayBuffer)
    const modulesJsonLength = modulesDataView.getUint32(0, false)
    const modulesJsonBytes = new Uint8Array(
      modulesArrayBuffer,
      4,
      modulesJsonLength
    )
    const modulesJsonString = new TextDecoder('utf-8').decode(modulesJsonBytes)
    this.modulesHeader = JSON.parse(modulesJsonString) as ModulesDataHeader
    const modulesBinaryOffset = 4 + modulesJsonLength
    this.modulesBinaryData = new DataView(
      modulesArrayBuffer,
      modulesBinaryOffset
    )

    // Build pathToModuleIndex map
    this.pathToModuleIndex = new Map()
    for (let i = 0; i < this.modulesHeader.modules.length; i++) {
      const module = this.modulesHeader.modules[i]
      const existing = this.pathToModuleIndex.get(module.path)
      if (existing) {
        existing.push(i)
      } else {
        this.pathToModuleIndex.set(module.path, [i])
      }
    }
  }

  moduleIndexHash(): string {
    const hash = this.modulesHeader.module_index_hash
    if (typeof hash !== 'string' || !hash) {
      throw new Error('Missing analyzer module-index fingerprint')
    }
    return hash
  }

  module(index: ModuleIndex): AnalyzeModule | undefined {
    return this.modulesHeader.modules[index]
  }

  moduleCount(): number {
    return this.modulesHeader.modules.length
  }

  getModuleIndiciesFromPath(path: string): ModuleIndex[] {
    return this.pathToModuleIndex.get(path) ?? []
  }
…
export interface FileNode {
  id: number
  name: string
  type: 'file'
  size: number
  outputType: 'code' | 'css' | 'asset'
  server: boolean
  client: boolean
  dependencies: number[]
  dependents: number[]
}

export interface DirectoryNode {
  id: number
  name: string
  type: 'directory'
  children: TreeNode[]
  size?: number
}
```
Sources: [apps/bundle-analyzer/lib/analyze-data.ts:62-115](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/lib/analyze-data.ts#L62-L115), [apps/bundle-analyzer/lib/types.ts:1-19](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/lib/types.ts#L1-L19)

> [!WARNING]
> Joining a route analysis data payload with an unmatched global module data buffer throws a runtime fingerprint mismatch error to prevent corrupted graph traversals.

```typescript
const moduleHash = modules.moduleIndexHash()
const routeHash = route.moduleIndexHash()
if (moduleHash !== routeHash) {
  throw new Error(`${side}: analyzer module-index fingerprint mismatch`)
}
```
Sources: [apps/bundle-analyzer/lib/analyze-data.ts:221-225](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/apps/bundle-analyzer/lib/analyze-data.ts#L221-L225)

## Core Package Organization

### App Directory and Module Resolution

The main `next` package tree is structured into server-side runtime infrastructure, compilation layers, testing utilities, and module resolution helpers. Layer separation isolates bundling internals from runtime routing modules and experimental test harnesses.

Server module resolution for the [App Router](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-runtime/app-router) relies on `LoaderTree` tuples generated by internal [webpack loaders](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/build-system/webpack-loaders). These trees encode segments, parallel routing keys, static sibling metadata, and module definitions to load layouts, pages, and fallback states dynamically.

```typescript
export type LoaderTree = [
  segment: string,
  parallelRoutes: { [parallelRouterKey: string]: LoaderTree },
  modules: AppDirModules,
  staticSiblings: readonly string[] | null,
]
```
Sources: [packages/next/src/server/lib/app-dir-module.ts:7-29](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/lib/app-dir-module.ts#L7-L29)

The server retrieves layouts and page modules through `getLayoutOrPageModule`, evaluating segment types and default fallbacks based on the active `DEFAULT_SEGMENT_KEY`.

```typescript
export async function getLayoutOrPageModule(loaderTree: LoaderTree) {
  const { layout, page, defaultPage } = loaderTree[2]
  const isLayout = typeof layout !== 'undefined'
  const isPage = typeof page !== 'undefined'
  const isDefaultPage =
    typeof defaultPage !== 'undefined' && loaderTree[0] === DEFAULT_SEGMENT_KEY

  let mod = undefined
  let modType: 'layout' | 'page' | undefined = undefined
  let filePath = undefined

  if (isLayout) {
    mod = await layout[0]()
    modType = 'layout'
    filePath = layout[1]
  } else if (isPage) {
    mod = await page[0]()
    modType = 'page'
    filePath = page[1]
  } else if (isDefaultPage) {
    mod = await defaultPage[0]()
    modType = 'page'
    filePath = defaultPage[1]
  }

  return { mod, modType, filePath }
}
```
Sources: [packages/next/src/server/lib/app-dir-module.ts:31-57](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/lib/app-dir-module.ts#L31-L57)

> [!NOTE]
> `getComponentTypeModule` accepts specific module types (`layout`, `not-found`, `forbidden`, `unauthorized`) to resolve conditional error and boundary components directly from the loader tree structure.

```typescript
export async function getComponentTypeModule(
  loaderTree: LoaderTree,
  moduleType: 'layout' | 'not-found' | 'forbidden' | 'unauthorized'
) {
  const { [moduleType]: module } = loaderTree[2]
  if (typeof module !== 'undefined') {
    return await module[0]()
  }
  return undefined
}
```
Sources: [packages/next/src/server/lib/app-dir-module.ts:59-68](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/lib/app-dir-module.ts#L59-L68)

### Experimental Testing and Shared Bundles

The package exposes experimental testing entry points for server configurations and middleware behaviors, alongside specialized routing query utilities.

| Export Module | Source Path | Responsibilities |
| :--- | :--- | :--- |
| `config-testing-utils` | `./config-testing-utils` | Testing utilities for server configuration options |
| `middleware-testing-utils` | `./middleware-testing-utils` | Testing harnesses for middleware request execution |
| Route Utilities | `./utils` | Exports `getRedirectUrl`, `getRewrittenUrl`, and `isRewrite` |

```typescript
export * from './config-testing-utils'
export * from './middleware-testing-utils'
export { getRedirectUrl, getRewrittenUrl, isRewrite } from './utils'
```
Sources: [packages/next/src/experimental/testing/server/index.ts:1-3](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/experimental/testing/server/index.ts#L1-L3)

Shared app-route runtime modules re-export context shims such as `appRouterContext` to maintain compatibility with navigation hooks imported from `next/navigation`.

```typescript
// the name of the export has to be the camelCase version of the file name (without the extension)
// TODO: remove this. We need it because using notFound from next/navigation imports this file :(
export * as appRouterContext from '../../../shared/lib/app-router-context.shared-runtime'
```
Sources: [packages/next/src/server/route-modules/app-route/shared-modules.ts:1-3](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/server/route-modules/app-route/shared-modules.ts#L1-L3)

## Build System and Asset Tracing

### Build Adapters and File Tracing

The Next.js build infrastructure includes asset tracing modules that process `.nft.json` trace files generated by bundlers like Webpack and Turbopack. The `mapNftFileEntries` helper parses `NftJson` structures, mapping source paths against base roots and additional roots while managing symlinks and root escapes.

```typescript
export interface MappedNftFileEntry {
  source: string
  destination: string
  hash?: string
  symlinkTarget?: string
  symlinkCrossesRoot: boolean
}
```
Sources: [packages/next/src/build/nft.ts:112-123](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/nft.ts#L112-L123)

When building outputs, `pushAsset` records assets and generates SHA-256 hashes when utilizing Turbopack. File hashing inspects symlinks via `fs.readlink` or falls back to reading file buffers directly with a salt prefix.

| Helper Function | Purpose |
| :--- | :--- |
| `loadNFT` | Parses `.nft.json` payloads and registers mapped entries |
| `hashFile` | Computes a salted SHA-256 hash for files or symlink targets |
| `hashLinkTarget` | Generates a salted SHA-256 hash for link target strings |
| `resolveNftOutputPath` | Verifies that destination paths remain inside deployment roots |

```typescript
async function pushAsset(
  assets: Record<string, string>,
  assetsHashes: Record<string, string>,
  targetFilePath: string,
  sourceFilePath: string,
  bundler: Bundler,
  salt: string,
  hashOverride?: string
) {
  if (!(targetFilePath in assets)) {
    assets[targetFilePath] = sourceFilePath
    if (bundler === Bundler.Turbopack) {
      assetsHashes[targetFilePath] =
        hashOverride ?? (await hashFile(salt, sourceFilePath))
    }
  }
}

async function loadNFT(
  assets: Record<string, string>,
  assetsHashes: Record<string, string>,
  repoRoot: string,
  traceFilePath: string,
  syntheticSymlinks: SyntheticSymlinkManager,
  salt: string
): Promise<{ entryHash?: string }> {
  const nft = JSON.parse(await fs.readFile(traceFilePath, 'utf8')) as NftJson

  for (const entry of mapNftFileEntries(nft, traceFilePath, repoRoot)) {
    let source = entry.source
    let hash = entry.hash

    if (entry.symlinkCrossesRoot) {
      if (entry.symlinkTarget === undefined) {
        throw new InvariantError(
          `Expected cross-root symlink ${JSON.stringify(entry.destination)} to have a target`
        )
      }
      const linkTarget =
        path.relative(path.dirname(entry.destination), entry.symlinkTarget) ||
        '.'
      hash = hashLinkTarget(salt, linkTarget)
      source = syntheticSymlinks.createLink(entry.source, linkTarget, hash)
    }

    assets[entry.destination] = source
    if (hash) {
      assetsHashes[entry.destination] = hash
    }
  }
  return { entryHash: nft.entryHash }
}

async function hashFile(salt: string, filePath: string): Promise<string> {
  try {
    // Try symlink first, since readFile just transparently resolves those (or fails if it's a
    // directory symlink).
    const linkTarget = await fs.readlink(filePath)
    return hashLinkTarget(salt, linkTarget)
  } catch (e: any) {
    if (e.code === 'EINVAL') {
      // Not a symlink
      const hash = crypto.createHash('sha256')
      hash.update(salt)
      hash.update('file:')
      hash.update(await fs.readFile(filePath))
      return hash.digest('hex')
    } else {
      throw e
    }
  }
}

function hashLinkTarget(salt: string, linkTarget: string): string {
  const hash = crypto.createHash('sha256')
  hash.update(salt)
  hash.update('link')
  hash.update(linkTarget)
  return hash.digest('hex')
```
Sources: [packages/next/src/build/adapter/build-complete.ts:2822-2900](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/adapter/build-complete.ts:2822-2900)

> [!CAUTION]
> If a file or symlink destination attempts to escape the deployment root during resolution, `resolveNftOutputPath` immediately throws an invariant error.

```typescript
export function resolveNftOutputPath(
  outputRoot: string,
  destination: string
): string {
  const outputPath = path.resolve(outputRoot, destination)
  if (relativePathIfInside(outputRoot, outputPath) === undefined) {
    invalid(
      `output path ${JSON.stringify(destination)} escapes the deployment root`
    )
  }
  return outputPath
}
```
Sources: [packages/next/src/build/nft.ts:269-280](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/nft.ts#L269-L280)

### Turborepo Access Tracing

The Turbopack and Turborepo integration exposes access tracing utilities that record file system operations during compilation and build passes. The access trace subsystem is exported via modular entry points containing result classes and helper functions.

```typescript
export type { SerializableTurborepoAccessTraceResult } from './types'
export {
  turborepoTraceAccess,
  writeTurborepoAccessTraceResult,
} from './helpers'
export { TurborepoAccessTraceResult } from './result'
```
Sources: [packages/next/src/build/turborepo-access-trace/index.ts:1-6](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/turborepo-access-trace/index.ts#L1-L6)

In parallel, build analysis graph dumping utilities (`dumpAnalyzeGraph`) read module indices and route snapshots to validate binary edge representations and schema versions.

```typescript
export function dumpAnalyzeGraph(
  directory: string,
  snapshotName: string,
  routeFilter: string | undefined
): void {
  // Pipe stdout must block so writeSync supplies backpressure rather than EAGAIN.
  if (process.stdout._handle != null) {
    // @ts-ignore Node's internal handle, also used by the native bindings loader.
    process.stdout._handle.setBlocking?.(true)
  }
```
Sources: [packages/next/src/build/analyze/graph-dump.ts:326-335](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/build/analyze/graph-dump.ts#L326-L335)

## Developer Tooling and Overlay Subsystems

### Devtools Entrypoints and Browser Overlays

The Next.js developer tooling subsystem initializes browser error overlays, build status indicators, and segment trees via entrypoints housed in `packages/next/src/next-devtools`. The main dev-overlay browser module (`packages/next/src/next-devtools/dev-overlay.browser.tsx`) exports `renderAppDevOverlay` and `renderPagesDevOverlay`, which attach shadow roots and mount the `DevOverlayRoot` component inside isolated `nextjs-portal` containers.

| Function | Router Type | Key Parameters | Purpose |
| :--- | :--- | :--- | :--- |
| `renderAppDevOverlay` | `app` | `getOwnerStack`, `isRecoverableError`, `enableCacheIndicator`, `enableRuntimeErrorReporting` | Mounts the [developer overlay](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/development/developer-overlay) for the App Router with a script container and identifier prefix `ndt-` |
| `renderPagesDevOverlay` | `pages` | `getOwnerStack`, `getSquashedHydrationErrorDetails`, `isRecoverableError` | Mounts the developer overlay for the Pages Router using a `MutationObserver` to re-attach the container if removed |

```typescript
export * from './dev-overlay.browser'
```
Sources: [packages/next/src/next-devtools/entrypoint.ts:1-1](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/entrypoint.ts#L1-L1)

> [!CAUTION]
> Attempting to mount the Pages developer overlay while the App Router overlay is already mounted, or vice versa, triggers an invariant error because switching between router types requires a hard navigation.

```tsx
if (isPagesMounted) {
  // Switching between App and Pages Router is always a hard navigation
  // TODO: Support soft navigation between App and Pages Router
  throw new Error(
    'Next DevTools: Pages Dev Overlay is already mounted. This is a bug in Next.js'
  )
}

```
Sources: [packages/next/src/next-devtools/dev-overlay.browser.tsx:455-462](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/dev-overlay.browser.tsx#L455-L462)

The `dispatcher` object processes queued events through an `EventQueue` instance, mapping action dispatchers for build states, version info, indicators, and runtime errors.

| Dispatcher Action | Action Type Constant | Description |
| :--- | :--- | :--- |
| `onBuildOk` | `ACTION_BUILD_OK` | Signals successful compilation |
| `onBuildError` | `ACTION_BUILD_ERROR` | Reports build error messages |
| `onBeforeRefresh` | `ACTION_BEFORE_REFRESH` | Triggers before hot module replacement refresh |
| `onRefresh` | `ACTION_REFRESH` | Executes HMR refresh |
| `onStaticIndicator` | `ACTION_STATIC_INDICATOR` | Updates static vs [dynamic rendering](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/server-runtime/dynamic-rendering) status (`pending`, `static`, `dynamic`, `disabled`) |
| `openErrorOverlay` | `ACTION_ERROR_OVERLAY_OPEN` | Opens the runtime error overlay |

```tsx
export const dispatcher: Dispatcher = {
  onBuildOk: createQueuable((dispatch: Dispatch) => {
    dispatch({ type: ACTION_BUILD_OK })
  }),
  onBuildError: createQueuable((dispatch: Dispatch, message: string) => {
    dispatch({ type: ACTION_BUILD_ERROR, message })
  }),
  onBeforeRefresh: createQueuable((dispatch: Dispatch) => {
    dispatch({ type: ACTION_BEFORE_REFRESH })
  }),
  onRefresh: createQueuable((dispatch: Dispatch) => {
    dispatch({ type: ACTION_REFRESH })
  }),
```
Sources: [packages/next/src/next-devtools/dev-overlay.browser.tsx:203-215](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/dev-overlay.browser.tsx#L203-L215)

### Segment Trees and User-Space Hooks

The dev overlay includes a trie data structure located in `packages/next/src/next-devtools/dev-overlay/segment-explorer-trie.ts` for storing and searching App Router segment paths. The trie utilizes null-prototype child objects (`Object.create(null)`) so that segment names colliding with built-in `Object.prototype` members like `constructor` or `toString` resolve correctly.

```typescript
type TrieNode<Value = string> = {
  value: Value | undefined
  children: {
    [key: string]: TrieNode<Value> | undefined
  }
}
```
Sources: [packages/next/src/next-devtools/dev-overlay/segment-explorer-trie.ts:21-26](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/dev-overlay/segment-explorer-trie.ts#L21-L26)

User-space integration hooks are provided in `packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx`, exporting boundary components and state providers.

| Exported Component / Hook | Purpose |
| :--- | :--- |
| `SegmentStateProvider` | Context provider managing boundary types (`not-found`, `error`, `loading`, `global-error`) and reload triggers |
| `SegmentBoundaryTriggerNode` | Renders simulated fallback components such as `LoadingSegmentNode`, `NotFoundSegmentNode`, or `ErrorSegmentNode` |
| `SegmentViewStateNode` | Updates route state trees via the dispatcher during layout rendering |
| `useSegmentState` | Consumes segment boundary context |

```tsx
export function SegmentStateProvider({ children }: { children: ReactNode }) {
  const [boundaryType, setBoundaryType] = useState<SegmentBoundaryType | null>(
    null
  )

  const [errorBoundaryKey, setErrorBoundaryKey] = useState(0)
  const reloadBoundary = useCallback(
    () => setErrorBoundaryKey((prev) => prev + 1),
    []
  )
```
Sources: [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx:129-138](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx#L129-L138)

> [!NOTE]
> `SegmentTrieNode` registers node state using `useLayoutEffect` rather than `useEffect` to ensure updates occur during React Suspense boundaries without losing mounted state.

```tsx
// Use `useLayoutEffect` to ensure the state is updated during suspense.
// `useEffect` won't work as the state is preserved during suspense.
useLayoutEffect(() => {
  dispatcher.segmentExplorerNodeAdd(nodeState)
  return () => {
    dispatcher.segmentExplorerNodeRemove(nodeState)
  }
}, [nodeState])
```
Sources: [packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx:50-57](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/userspace/app/segment-explorer-node.tsx#L50-L57)

Additional user-space error helpers are re-exported from `packages/next/src/next-devtools/userspace/app/errors/index.ts`, exposing console error interception, client error handling, and dev error decoration.

```typescript
export { originConsoleError } from './intercept-console-error'
export { handleClientError } from './use-error-handler'
export { decorateDevError } from './stitched-error'
```
Sources: [packages/next/src/next-devtools/userspace/app/errors/index.ts:1-3](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/next-devtools/userspace/app/errors/index.ts#L1-L3)

## Auxiliary Tooling Packages

### Documentation Indexing and Tree Building

Satellite packages within the repository provide auxiliary capabilities such as repository automation, documentation indexing, and code transformations via `packages/next-codemod`. This package includes utilities for discovering documentation files, parsing directory trees, generating agent-readable indexes, and managing workspace configurations.

The codemod package contains routines in `packages/next-codemod/lib/agents-md.ts` that scan documentation directories to build hierarchical representations of markdown and MDX files. The file collector filters out index pages, sorts results lexicographically, and normalizes path separators for cross-platform compatibility.

```typescript
export function collectDocFiles(dir: string): { relativePath: string }[] {
  return (fs.readdirSync(dir, { recursive: true }) as string[])
    .filter(
      (f) =>
        (f.endsWith('.mdx') || f.endsWith('.md')) &&
        !/[/\\]index\.mdx$/.test(f) &&
        !/[/\\]index\.md$/.test(f) &&
        !f.startsWith('index.')
    )
    .sort()
    .map((f) => ({ relativePath: f.replace(/\\/g, '/') }))
}
```
Sources: [packages/next-codemod/lib/agents-md.ts:237-248](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-codemod/lib/agents-md.ts#L237-L248)

The tree builder organizes files into nested `DocSection` structures up to three levels deep, sorting sections and subsections alphabetically.

| Structure Property | Type | Description |
| :--- | :--- | :--- |
| `name` | `string` | Directory segment name |
| `files` | `{ relativePath: string }[]` | List of documentation files contained in the section |
| `subsections` | `DocSection[]` | Nested child sections |

```typescript
interface DocSection {
  name: string
  files: { relativePath: string }[]
  subsections: DocSection[]
}
```
Sources: [packages/next-codemod/lib/agents-md.ts:250-254](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-codemod/lib/agents-md.ts#L250-L254)

> [!NOTE]
> `collectDocFiles` explicitly ignores any file whose path ends with `index.md` or `index.mdx`, or starts with `index.`, to prevent top-level landing pages from duplicating nested route structures in agent indexes.

```typescript
return (fs.readdirSync(dir, { recursive: true }) as string[])
  .filter(
    (f) =>
      (f.endsWith('.mdx') || f.endsWith('.md')) &&
      !/[/\\]index\.mdx$/.test(f) &&
      !/[/\\]index\.md$/.test(f) &&
      !f.startsWith('index.')
  )
```
Sources: [packages/next-codemod/lib/agents-md.ts:238-245](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next.js/blob/HEAD/packages/next-codemod/lib/agents-md.ts#L238-L245)

### Workspace Detection and Ignore Management

The auxiliary package inspects project environments to identify monorepo structures and ensure generated build artifacts or documentation caches are properly excluded from version control.

| Workspace Type | Detection File | Parsing Utility |
| :--- | :--- | :--- |
| `pnpm` | `pnpm-workspace.yaml` | `parsePnpmWorkspace` |
| `npm` / `yarn` | `package.json` | `parsePackageJsonWorkspaces` |
| `lerna` | `lerna.json` | `parseLernaConfig` |
| `null` | none | none |

```typescript
function detectWorkspace(cwd: string): WorkspaceInfo {
  const packageJsonPath = path.join(cwd, 'package.json')

  // Check pnpm workspaces (pnpm-workspace.yaml)
  const pnpmWorkspacePath = path.join(cwd, 'pnpm-workspace.yaml')
  if (fs.existsSync(pnpmWorkspacePath)) {
    const packages = parsePnpmWorkspace(pnpmWorkspacePath)
    if (packages.length > 0) {
      return { isMonorepo: true, type: 'pnpm', packages }
    }
  }

  // Check npm/yarn workspaces (package.json workspaces field)
  if (fs.existsSync(packageJsonPath)) {
    const packages = parsePackageJsonWorkspaces(packageJsonPath)
    if (packages.length > 0) {
      return { isMonorepo: true, type: 'npm', packages }
    }
  }

  // Check Lerna (lerna.json)
  const lernaPath = path.join(cwd, 'lerna.json')
  if (fs.existsSync(lernaPath)) {
    const packages = parseLernaConfig(lernaPath)
    if (packages.length > 0) {
      return { isMonorepo: true, type: 'lerna', packages }
```
Sources: [packages/next-codemod/lib/agents-md.ts:463-488](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-codemod/lib/agents-md.ts#L463-L488)

When updating `.gitignore`, `ensureGitignoreEntry` checks for existing patterns matching `.next-docs` entries using regular expression tests across file lines, appending the entry and a comment header if absent.

```typescript
export function ensureGitignoreEntry(cwd: string): GitignoreStatus {
  const gitignorePath = path.join(cwd, '.gitignore')
  const entryRegex = /^\s*\.next-docs(?:\/.*)?$/

  let content = ''
  if (fs.existsSync(gitignorePath)) {
    content = fs.readFileSync(gitignorePath, 'utf-8')
  }

  const hasEntry = content.split(/\r?\n/).some((line) => entryRegex.test(line))

  if (hasEntry) {
    return { path: gitignorePath, updated: false, alreadyPresent: true }
  }
...
```
Sources: [packages/next-codemod/lib/agents-md.ts:428-441](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-codemod/lib/agents-md.ts#L428-L441)

## Related

- [Overview](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/orientation/overview)
- [Monorepo Workspace](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/orientation/monorepo-workspace)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/llms.txt) for all pages in this wiki.
