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

Route Discovery

Last updated Oct 12, 2026100% grounded
Relevant source files

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

Overview

Route Discovery handles locating, scanning, and processing all file-based endpoints across the pages and app directories, turning filesystem conventions into structured route metadata and manifests packages/next/src/build/route-discovery.ts:101-131, packages/next/src/server/lib/router-utils/filesystem.ts:372-396. It serves as the foundational pipeline layer that unifies production builds and Build Pipeline initialization by extracting route modules, parallel slots, layout trees, and API handlers packages/next/src/build/index.ts:1658-1669, packages/next/src/server/lib/router-utils/route-types-utils.ts:214-236. By validating parameter syntax, preventing routing conflicts, and parsing segment-level configuration exports, the component ensures runtime and compile-time consistency packages/next/src/build/validate-app-paths.ts:202-278, packages/next/src/build/segment-config/app/app-segments.ts:183-286. Ultimately, it coordinates synchronization with development filesystem watchers and serializes static analysis artifacts and route type manifests packages/next/src/build/index.ts:1646-1687, packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:725-814.

Filesystem Scanning and Route Collection

Scanning and Collecting App Directory Files

The collectAppFiles function scans the designated app directory recursively to extract paths for application pages, layouts, and default entries packages/next/src/build/route-discovery.ts:101-131. It relies on recursiveReadDir with a pathnameFilter checking valid app router pages, root not-found boundaries, app layout pages, and default pages, while ignoring any file or folder paths starting with an underscore via ignorePartFilter packages/next/src/build/route-discovery.ts:109-116. Once the raw file list is retrieved, filter predicates segregate them into distinct collections for application paths, layout paths, and default paths packages/next/src/build/route-discovery.ts:118-130.

export async function collectAppFiles(
  appDir: string,
  validFileMatcher: ReturnType<typeof createValidFileMatcher>
): Promise<{
  appPaths: string[]
  layoutPaths: string[]
  defaultPaths: string[]
}> {
  const allAppFiles = await recursiveReadDir(appDir, {
    pathnameFilter: (absolutePath) =>
      validFileMatcher.isAppRouterPage(absolutePath) ||
      validFileMatcher.isRootNotFound(absolutePath) ||
      validFileMatcher.isAppLayoutPage(absolutePath) ||
      validFileMatcher.isAppDefaultPage(absolutePath),
    ignorePartFilter: (part) => part.startsWith('_'),
  })
 
  const appPaths = allAppFiles.filter(
    (absolutePath) =>
      validFileMatcher.isAppRouterPage(absolutePath) ||
      validFileMatcher.isRootNotFound(absolutePath)
  )
  const layoutPaths = allAppFiles.filter((absolutePath) =>
    validFileMatcher.isAppLayoutPage(absolutePath)
  )
  const defaultPaths = allAppFiles.filter((absolutePath) =>
    validFileMatcher.isAppDefaultPage(absolutePath)
  )
 
  return { appPaths, layoutPaths, defaultPaths }
}

Sources: packages/next/src/build/route-discovery.ts:101-131

Scanning Pages Directory and Path Normalization

The collectPagesFiles function queries the traditional pages directory utilizing recursiveReadDir filtered specifically by validFileMatcher.isPageFile packages/next/src/build/route-discovery.ts:136-143. To transform raw filesystem absolute paths into logical routes or relative entries, helper functions such as getPageFromPath, normalizeAppRoute, and normalizeLayoutRoute process path strings by sorting and removing extensions by decreasing length and stripping router segment suffixes packages/next/src/build/route-discovery.ts:63-96.

export function getPageFromPath(
  pagePath: string,
  pageExtensions: PageExtensions
) {
  let page = normalizePathSep(pagePath)
  // Try longer extensions first so compound extensions like 'page.js'
  // match before shorter ones like 'js'
  const sorted = [...pageExtensions].sort((a, b) => b.length - a.length)
  for (const extension of sorted) {
    const next = removeSuffix(page, `.${extension}`)
    if (next !== page) {
      page = next
      break
    }
  }
 
  page = removeSuffix(page, '/index')
 
  return page === '' ? '/' : page
}

Sources: packages/next/src/build/route-discovery.ts:77-96

Note

Extension sorting within getPageFromPath sorts pageExtensions by descending length (b.length - a.length) to ensure compound extensions like .page.js take precedence over shorter substrings like .js.

Sources: packages/next/src/build/route-discovery.ts:84-91

Processing Mapped Routes and Relative File Paths

The route processing pipeline converts mapped route dictionaries into structured RouteInfo arrays via processPageRoutes and processAppRoutes packages/next/src/build/route-discovery.ts:166-214. Each mapped entry constructs a relative file path using createRelativeFilePath, applying prefix regular expressions (PRIVATE_PAGES_PREFIX_REGEX or PRIVATE_APP_PREFIX_REGEX) alongside source directory indicators packages/next/src/build/route-discovery.ts:35-36, packages/next/src/build/route-discovery.ts:148-161.

export function createRelativeFilePath(
  baseDir: string,
  filePath: string,
  prefix: 'pages' | 'app',
  isSrcDir: boolean
): string {
  const privatePrefixRegex =
    prefix === 'pages' ? PRIVATE_PAGES_PREFIX_REGEX : PRIVATE_APP_PREFIX_REGEX
  const srcPrefix = isSrcDir ? 'src/' : ''
  return join(
    baseDir,
    filePath.replace(privatePrefixRegex, `${srcPrefix}${prefix}/`)
  )
}

Sources: packages/next/src/build/route-discovery.ts:148-161

Prefix TypeInternal Regex ConstantTarget Directory Replacement
pagesPRIVATE_PAGES_PREFIX_REGEX (/^private-next-pages\//)[src/]pages/
appPRIVATE_APP_PREFIX_REGEX (/^private-next-app-dir\//)[src/]app/

Sources: packages/next/src/build/route-discovery.ts:35-36, packages/next/src/build/route-discovery.ts:154-160

Route Segments and Slot Classification

Parallel Route Slot Extraction

Parallel routes in the App Router are identified by slot folders prefixed with an @ symbol. The classification engine extracts slot metadata from path strings by iterating backward through path segments until it encounters a parallel route segment verified by isParallelRouteSegment packages/next/src/build/file-classifier.ts:13-25. The resulting SlotInfo structure pairs the normalized parent route path with the slot name stripped of its leading @ character packages/next/src/build/file-classifier.ts:5-6, packages/next/src/build/file-classifier.ts:18-20.

export type SlotInfo = { name: string; parent: string }
 
// Route information for route manifests
export type RouteInfo = { route: string; filePath: string }
 
/**
 * Extract slot info from a page path if it contains a parallel route
 */
export function extractSlotFromPath(pagePath: string): SlotInfo | undefined {
  const segments = pagePath.split('/')
  for (let i = segments.length - 1; i >= 0; i--) {
    const segment = segments[i]
    if (isParallelRouteSegment(segment)) {
      return {
        name: segment.slice(1),
        parent: normalizeAppPath(segments.slice(0, i).join('/')),
      }
    }
  }
  return undefined
}

Sources: packages/next/src/build/file-classifier.ts:5-25

Important

The parent path for a parallel route slot is computed by slicing path segments preceding the slot directory and passing them through normalizeAppPath, ensuring dynamic catch-all and optional parameters map correctly to their logical route parent.

Sources: packages/next/src/build/file-classifier.ts:1-2, packages/next/src/build/file-classifier.ts:13-22

Slot Collection and Deduplication

When aggregating slots across multiple route entries or route mapping dictionaries, the classifier filters out skipped routes and checks for uniqueness based on a composite key format combining slot name and parent path (${slot.name}:${slot.parent}) packages/next/src/build/file-classifier.ts:30-70. Functions such as addSlotIfNew, extractSlotsFromRoutes, and combineSlots maintain distinct slot collections by rejecting duplicate entries that share identical parent contexts and slot identifiers packages/next/src/build/file-classifier.ts:30-70.

export function combineSlots(...slotArrays: SlotInfo[][]): SlotInfo[] {
  const slotSet = new Set<string>()
  const result: SlotInfo[] = []
  for (const slots of slotArrays) {
    for (const slot of slots) {
      const key = `${slot.name}:${slot.parent}`
      if (!slotSet.has(key)) {
        slotSet.add(key)
        result.push(slot)
      }
    }
  }
  return result
}

Sources: packages/next/src/build/file-classifier.ts:57-70

File Path Normalization and Convention Boundaries

File path classification handles project root directories, monorepo workspaces, and Turbopack path prefixes by stripping markers such as [project] or absolute working directories through normalizeFilePath packages/next/src/server/app-render/segment-explorer-path.ts:12-55. Convention files within the app directory are further processed via normalizeConventionFilePath to remove app/ or src/app/ prefixes, while built-in internal components matching nextInternalPrefixRegex receive a __next_builtin__ prefix packages/next/src/server/app-render/segment-explorer-path.ts:5-7, packages/next/src/server/app-render/segment-explorer-path.ts:57-73.

export function normalizeConventionFilePath(
  projectDir: string,
  conventionPath: string | undefined
) {
  let relativePath = normalizeFilePath(projectDir, conventionPath)
    // remove /(src/)?app/ dir prefix
    .replace(/^(src\/)?app\//, '')
 
  // If it's internal file only keep the filename, strip nextjs internal prefix
  if (nextInternalPrefixRegex.test(relativePath)) {
    relativePath = relativePath.replace(nextInternalPrefixRegex, '')
    // Add a special prefix to let segment explorer know it's a built-in component
    relativePath = `${BUILTIN_PREFIX}${relativePath}`
  }
 
  return relativePath
}

Sources: packages/next/src/server/app-render/segment-explorer-path.ts:57-73

Constant NameValuePurpose
BUILTIN_PREFIX__next_builtin__Identifies internal built-in components like global error boundaries.
BOUNDARY_SUFFIX@boundarySuffix marker trimmed during boundary filename normalization.
BOUNDARY_PREFIXboundary:Prefix identifying boundary file types in segment exploration trees.

Sources: packages/next/src/server/app-render/segment-explorer-path.ts:3, packages/next/src/server/app-render/segment-explorer-path.ts:82, packages/next/src/server/app-render/segment-explorer-path.ts:89

App Path Validation and Matching

Segment Parameter Validation Invariants

The validateSegmentParam function inspects dynamic segment parameters against strict syntax and naming rules packages/next/src/build/validate-app-paths.ts:16-53. It enforces that parameter names are non-empty, reject illegal three-dot characters (…), guard against unescaped or unsupported optional syntax, and prohibit leading or trailing brackets or erroneous periods packages/next/src/build/validate-app-paths.ts:16-53.

function validateSegmentParam(param: SegmentParam, pathname: string): void {
  // Check for empty parameter names
  if (param.paramName.length === 0) {
    throw new Error(`Parameter names cannot be empty in route "${pathname}".`)
  }
 
  // Check for three-dot character (…) instead of ...
  if (param.paramName.includes('…')) {
    throw new Error(
      `Detected a three-dot character ('…') in parameter "${param.paramName}" in route "${pathname}". Did you mean ('...')?`
    )
  }
 
  // Check for optional non-catch-all segments (not yet supported)
  if (
    param.paramType !== 'optional-catchall' &&
    param.paramName.startsWith('[') &&
    param.paramName.endsWith(']')
  ) {
    throw new Error(
      `Optional route parameters are not yet supported ("[${param.paramName}]") in route "${pathname}".`
    )
  }
 
  // Check for extra brackets
  if (param.paramName.startsWith('[') || param.paramName.endsWith(']')) {
    throw new Error(
      `Segment names may not start or end with extra brackets ('${param.paramName}') in route "${pathname}".`
    )
  }
 
  // Check for erroneous periods
  if (param.paramName.startsWith('.')) {
    throw new Error(
      `Segment names may not start with erroneous periods ('${param.paramName}') in route "${pathname}".`
    )
  }
}

Sources: packages/next/src/build/validate-app-paths.ts:16-53

Warning

Optional non-catch-all segments like [param] are not yet supported and will immediately throw an error during validateSegmentParam execution packages/next/src/build/validate-app-paths.ts:29-38.

Sources: packages/next/src/build/validate-app-paths.ts:29-38

Route Consistency and Conflict Detection

The validateAppRoute and validateAppPaths functions check whole route structures for duplicate slugs, invalid catch-all positioning, and cross-path ambiguities packages/next/src/build/validate-app-paths.ts:61-139, packages/next/src/build/validate-app-paths.ts:202-281. For interception routes, validation recursively verifies both intercepting and intercepted branches separately packages/next/src/build/validate-app-paths.ts:64-68.

function validateAppRoute(route: NormalizedAppRoute): void {
  // For interception routes, validate the intercepting and intercepted routes separately
  // This allows the same parameter name to appear in both parts
  if (isInterceptionAppRoute(route)) {
    validateAppRoute(route.interceptingRoute)
    validateAppRoute(route.interceptedRoute)
    return
  }

Sources: packages/next/src/build/validate-app-paths.ts:61-68

Error ConditionTrigger RuleTarget Route Context
Empty Parameterparam.paramName.length === 0Route pathname
Three-Dot Characterparam.paramName.includes('…')Route pathname
Optional Non-Catch-AllparamType !== 'optional-catchall' with bracketsRoute pathname
Extra BracketsStarts or ends with [ or ]Route pathname
Erroneous PeriodStarts with .Route pathname
Duplicate SlugSlug name re-used in dynamic pathRoute pathname
Slug Name CollisionNormalized slug names differ only by non-word symbolsRoute pathname
Mispositioned Catch-AllCatch-all or optional catch-all not at path endRoute pathname
Mixed Catch-All TypesBoth required and optional catch-all at same levelRoute pathname
Optional Catch-All SpecificitySame specificity as an optional catch-all route prefixApp paths / normalized structures
Ambiguous RoutesMultiple paths map to the same normalized structureConflict collection map

Sources: packages/next/src/build/validate-app-paths.ts:18-137, packages/next/src/build/validate-app-paths.ts:231-240, packages/next/src/build/validate-app-paths.ts:257-277

Caution

Slug names are normalized via .replace(/\W/g, '') before duplicate checking; slug names differing solely by non-word symbols within a single dynamic path will trigger a validation failure packages/next/src/build/validate-app-paths.ts:104-114.

Sources: packages/next/src/build/validate-app-paths.ts:104-114

Segment Configuration Parsing and Validation

Zod Schema Validation Architecture

Segment configuration parsing is implemented using Zod schemas to validate exported route segment options across app layouts, pages, and route handlers packages/next/src/build/segment-config/app/app-segment-config.ts:1-38. The main validator, AppSegmentConfigSchema, enforces strict typing on properties including revalidate, dynamic, fetchCache, runtime, maxDuration, preferredRegion, and unstable_dynamicStaleTime packages/next/src/build/segment-config/app/app-segment-config.ts:97-173.

const AppSegmentConfigSchema = z.object({
  /**
   * The number of seconds to revalidate the page or false to disable revalidation.
   */
  revalidate: z
    .union([z.number().int().nonnegative(), z.literal(false)])
    .optional(),
 
  /**
   * Whether the page supports dynamic parameters.
   */
  dynamicParams: z.boolean().optional(),
 
  /**
   * The dynamic behavior of the page.
   */
  dynamic: z
    .enum(['auto', 'error', 'force-static', 'force-dynamic'])
    .optional(),
 
  /**
   * The caching behavior of the page.
   */
  fetchCache: z
    .enum([
      'auto',
      'default-cache',
      'only-cache',
      'force-cache',
      'force-no-store',
      'default-no-store',
      'only-no-store',
    ])
    .optional(),
 
  /**
   * How this segment should be prefetched.
   */
  instant: InstantConfigSchema.optional(),
 
  /**
   * Controls prefetching for this segment.
   * - 'auto' (default) is a noop.
   * - 'partial' enables Partial Prefetching. Only Cache Components are
   *   prefetched, not dynamic ones. When a static prefetch is insufficient,
   *   the segment may be prefetched with a runtime request instead.
   * - 'force-disabled' disables prefetching for the segment.
   */
  prefetch: PrefetchSchema.optional(),
 
  /**
   * Controls which rendering phases require static output for this segment.
   */
  ensureStatic: EnsureStaticSchema.optional(),
 
  /**
   * The stale time for dynamic responses in seconds.
   * Controls how long the client-side router cache retains dynamic page data.
   * Pages only — not allowed in layouts.
   */
  unstable_dynamicStaleTime: z.number().int().nonnegative().optional(),
 
  /**
   * The preferred region for the page.
   */
  preferredRegion: z.union([z.string(), z.array(z.string())]).optional(),
 
  /**
   * The runtime to use for the page.
   */
  runtime: z.enum(['edge', 'nodejs']).optional(),
 
  /**
   * The maximum duration for the page in seconds.
   */
  maxDuration: z.number().int().nonnegative().optional(),
})

Sources: packages/next/src/build/segment-config/app/app-segment-config.ts:97-173

Warning

The unstable_dynamicStaleTime configuration property is restricted to pages only and is invalid when defined inside layout segments packages/next/src/build/segment-config/app/app-segment-config.ts:153-157.

Sources: packages/next/src/build/segment-config/app/app-segment-config.ts:153-157

Error Mapping and Parsing Execution

The parseAppSegmentConfig function evaluates userland exports against AppSegmentConfigSchema using a custom error map that inspects top-level Zod issue paths packages/next/src/build/segment-config/app/app-segment-config.ts:181-223. If validation fails, formatZodError formats the error message alongside documentation references packages/next/src/build/segment-config/app/app-segment-config.ts:225-230.

export function parseAppSegmentConfig(
  data: unknown,
  route: string
): AppSegmentConfig {
  const parsed = AppSegmentConfigSchema.safeParse(data, {
    errorMap: (issue, ctx) => {
      if (issue.path.length === 1) {
        switch (issue.path[0]) {
          case 'revalidate': {
            return {
              message: `Invalid revalidate value ${JSON.stringify(
                ctx.data
              )} on "${route}", must be a non-negative number or false`,
            }
          }
          case 'instant': {
            return {
              // @TODO replace this link with a link to the docs when they are written
              message: `Invalid instant value ${JSON.stringify(ctx.data)} on "${route}", must be \`true\`, \`false\`, or an object. Read more at https://nextjs.org/docs/messages/invalid-instant-configuration`,
            }
          }
          case 'prefetch': {
            return {
              message: `Invalid prefetch value ${JSON.stringify(ctx.data)} on "${route}", must be "auto", "partial", or "force-disabled".`,
            }
          }
          case 'ensureStatic': {
            return {
              message: `Invalid ensureStatic value ${JSON.stringify(ctx.data)} on "${route}", must be "auto", "shell", "prefetch", "navigation", or false.`,
            }
          }
          case 'unstable_dynamicStaleTime': {
            return {
              message: `Invalid unstable_dynamicStaleTime value ${JSON.stringify(ctx.data)} on "${route}", must be a non-negative number`,
            }
          }
          default:
        }
      }
 
      return { message: ctx.defaultError }
    },
  })
 
  if (!parsed.success) {
    throw formatZodError(
      `Invalid segment configuration options detected for "${route}". Read more at https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config`,
      parsed.error
    )
  }

Sources: packages/next/src/build/segment-config/app/app-segment-config.ts:181-230

Config PropertyZod Validation RuleError Message Path
revalidatez.union([z.number().int().nonnegative(), z.literal(false)]).optional()revalidate
dynamicz.enum(['auto', 'error', 'force-static', 'force-dynamic']).optional()Default zod error
fetchCachez.enum(['auto', 'default-cache', 'only-cache', 'force-cache', 'force-no-store', 'default-no-store', 'only-no-store']).optional()Default zod error
instantInstantConfigSchema.optional()instant
prefetchPrefetchSchema.optional()prefetch
ensureStaticEnsureStaticSchema.optional()ensureStatic
unstable_dynamicStaleTimez.number().int().nonnegative().optional()unstable_dynamicStaleTime
runtimez.enum(['edge', 'nodejs']).optional()Default zod error
maxDurationz.number().int().nonnegative().optional()Default zod error

Sources: packages/next/src/build/segment-config/app/app-segment-config.ts:97-173, packages/next/src/build/segment-config/app/app-segment-config.ts:187-216

Development Route Watching and Synchronization

Filesystem Watchers and Watchpack Integration

In development mode, Next.js relies on filesystem watchers powered by Watchpack to monitor app and pages directories for file changes, additions, and deletions packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:18. When changes occur in source directories, the development bundler re-scans filesystem paths and reconciles route definitions.

import Watchpack from 'next/dist/compiled/watchpack'

Sources: packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:18

Warning

Files or directories starting with _ inside the app directory are skipped during route collection, except for special convention files explicitly handled by valid file matchers packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:754-755.

Sources: packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:754-755

Conflict Detection and Error State Synchronization

During route synchronization, duplicate pages in the pages directory or conflicting paths between app and pages directories trigger error states via the hot reloader interface packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:819-869. The synchronization loop computes conflicting app page paths and updates the HMR server error status accordingly packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:883-901.

const numConflicting = conflictingAppPagePaths.size
conflictingPageChange = numConflicting - previousConflictingPagePaths.size
 
if (conflictingPageChange !== 0) {
  if (numConflicting > 0) {
    let errorMessage = `Conflicting app and page file${
      numConflicting === 1 ? ' was' : 's were'
    } found, please remove the conflicting files to continue:\n`
 
    for (const p of conflictingAppPagePaths) {
      const appPath = path.relative(dir, appPageFilePaths.get(p)!)
      const pagesPath = path.relative(dir, pagesPageFilePaths.get(p)!)
      errorMessage += `  "${pagesPath}" - "${appPath}"\n`
    }
    hotReloader.setHmrServerError(new Error(errorMessage))
  } else if (numConflicting === 0) {
    hotReloader.clearHmrServerError()
  }
}

Sources: packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:883-901

Caution

If conflicting app and page files are detected (numConflicting > 0), an error message is assigned to the hot reloader server, blocking compilation until the conflicting files are removed packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:886-897.

Sources: packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:886-897

Route Types Manifest Generation

Manifest Construction and Route Classification

The RouteTypesManifest structure aggregates classified entries from page routes, app routes, layouts, route handlers, redirects, and rewrites packages/next/src/server/lib/router-utils/route-types-utils.ts:38-57. During createRouteTypesManifest, custom route sources in redirects and rewrites are parsed using convertCustomRouteSource to normalize bracket and dynamic segments packages/next/src/server/lib/router-utils/route-types-utils.ts:62-66, packages/next/src/server/lib/router-utils/route-types-utils.ts:380-415. Each route entry retains an extracted parameter group map and relative file path computed against the project root or validator target directory packages/next/src/server/lib/router-utils/route-types-utils.ts:33-36, packages/next/src/server/lib/router-utils/route-types-utils.ts:238-247.

export interface RouteTypesManifest {
  appRoutes: Record<string, ManifestRouteInfo>
  pageRoutes: Record<string, ManifestRouteInfo>
  layoutRoutes: Record<string, ManifestRouteInfo & { slots: string[] }>
  appRouteHandlerRoutes: Record<string, ManifestRouteInfo>
  /** Map of redirect source => ManifestRouteInfo */
  redirectRoutes: Record<string, ManifestRouteInfo>
  /** Map of rewrite source => ManifestRouteInfo */
  rewriteRoutes: Record<string, ManifestRouteInfo>
  /** File paths for validation */
  appPagePaths: Set<string>
  pagesRouterPagePaths: Set<string>
  layoutPaths: Set<string>
  appRouteHandlers: Set<string>
  pageApiRoutes: Set<string>
  /** Direct mapping from file paths to routes for validation */
  filePathToRoute: Map<string, string>
  /** Root params collected from root layouts, with their possible runtime value types */
  rootParams: Map<string, RootParamInfo>
}

Sources: packages/next/src/server/lib/router-utils/route-types-utils.ts:38-57

Note

Internal application paths such as UNDERSCORE_GLOBAL_ERROR_ROUTE and UNDERSCORE_NOT_FOUND_ROUTE are explicitly filtered out when populating layoutRoutes and appRoutes manifest tables packages/next/src/server/lib/router-utils/route-types-utils.ts:315-319, packages/next/src/server/lib/router-utils/route-types-utils.ts:340-344.

Sources: packages/next/src/server/lib/router-utils/route-types-utils.ts:315-319, packages/next/src/server/lib/router-utils/route-types-utils.ts:340-344

Type and Validator Generation Modes

The type generator translates the manifest into TypeScript declarations, parameter union maps, and type verification wrappers packages/next/src/server/lib/router-utils/typegen.ts:459-521. Depending on config.experimental.strictRouteTypes, writeRouteTypesManifest selects between standard generation via generateRouteTypesFile and strict checking via generateRouteTypesFileStrict packages/next/src/server/lib/router-utils/route-types-utils.ts:435-440.

Validator FunctionTarget ConfigurationValidation Mechanism
generateValidatorFileAppPageConfig, LayoutConfig, RouteHandlerConfig, PagesPageConfig, ApiRouteConfigtype __Check = __IsExpected<typeof handler> with @ts-ignore bypass packages/next/src/server/lib/router-utils/typegen.ts:510-518
generateValidatorFileStrictAppPageConfig, LayoutConfig, RouteHandlerConfig, PagesPageConfig, ApiRouteConfighandler satisfies ${typeWithRoute} packages/next/src/server/lib/router-utils/typegen.ts:760-765

Sources: packages/next/src/server/lib/router-utils/typegen.ts:510-518, packages/next/src/server/lib/router-utils/typegen.ts:760-765

Caution

File validation strictly filters input paths to TypeScript extensions (.ts and .tsx), ignoring JavaScript files to avoid type inference limitations during boundary verification packages/next/src/server/lib/router-utils/typegen.ts:475-477, packages/next/src/server/lib/router-utils/typegen.ts:727-730.

Sources: packages/next/src/server/lib/router-utils/typegen.ts:475-477, packages/next/src/server/lib/router-utils/typegen.ts:727-730

Routes Manifest and Static Analysis Artifacts

Manifest Serialization and Header Configuration

The generateRoutesManifest function creates the primary routes-manifest.json structure, aggregating static and dynamic routes sorted via sortPages, custom redirects, headers, and rewrites packages/next/src/build/generate-routes-manifest.ts:53-118. It configures RSC request headers, vary headers, and prefetch routing metadata required by client-side navigation packages/next/src/build/generate-routes-manifest.ts:121-136.

Property NameSource / Value ExpressionPurpose
version3Defines manifest schema version compatibility packages/next/src/build/generate-routes-manifest.ts:98-98
pages404trueIndicates built-in fallback 404 page handling packages/next/src/build/generate-routes-manifest.ts:99-99
caseSensitive!!config.experimental.caseSensitiveRoutesToggles case-sensitive URL matching packages/next/src/build/generate-routes-manifest.ts:101-101
rsc.headerRSC_HEADERIdentifies React Server Component requests packages/next/src/build/generate-routes-manifest.ts:121-122
rsc.varyHeader${RSC_HEADER}, ${NEXT_ROUTER_STATE_TREE_HEADER}, ${NEXT_ROUTER_PREFETCH_HEADER}, ${NEXT_ROUTER_SEGMENT_PREFETCH_HEADER}Default vary header configuration for client router requests packages/next/src/build/generate-routes-manifest.ts:125-125
ppr{ chain: { headers: { [NEXT_RESUME_HEADER]: '1' } } }Configures Partial Prerendering header chains when enabled packages/next/src/build/generate-routes-manifest.ts:143-151

Sources: packages/next/src/build/generate-routes-manifest.ts:98-151

Note

When isAppPPREnabled is true, the manifest includes Partial Prerendering configuration rules with NEXT_RESUME_HEADER support, enabling partial streaming resumption during dynamic execution packages/next/src/build/generate-routes-manifest.ts:143-151.

Sources: packages/next/src/build/generate-routes-manifest.ts:143-151

Static Route Discovery and Compilation Analysis

Static route and bundle analysis utilities inspect build-time artifacts such as pages-manifest.json, app-paths-manifest.json, and middleware-manifest.json to classify route entries packages/next/src/cli/internal/static-routes-info.ts:212-230. The discovery process evaluates edge functions, server node bundles, and statically pre-rendered components without executing application code packages/next/src/cli/internal/static-routes-info.ts:231-307.

Sources: packages/next/src/cli/internal/static-routes-info.ts:212-307

Warning

Turbopack writes placeholder entry values such as app-edge-has-no-entrypoint inside manifests for Edge routes; static analysis must resolve actual bundle files directly from the middleware-manifest.json entry rather than depending on direct file paths packages/next/src/cli/internal/static-routes-info.ts:288-297.

Sources: packages/next/src/cli/internal/static-routes-info.ts:288-297

Was this page helpful?