Skip to content

feat: generate types for next/root-params during build and dev#90150

Draft
bgub wants to merge 1 commit intovercel:canaryfrom
bgub:feat/root-params-types
Draft

feat: generate types for next/root-params during build and dev#90150
bgub wants to merge 1 commit intovercel:canaryfrom
bgub:feat/root-params-types

Conversation

@bgub
Copy link
Contributor

@bgub bgub commented Feb 18, 2026

Summary

Generates a typed `declare module 'next/root-params'` declaration during `next build` and `next dev`, replacing the current blank placeholder in `packages/next/root-params.d.ts`.

What this does:

  • Identifies root layouts (shallowest layout in each app directory branch)
  • Extracts their dynamic segments with correct types
  • Writes `.next/types/root-params.d.ts` with typed getter functions
  • Includes it in the `routes.d.ts` entry file when `experimental.rootParams` or `cacheComponents` is enabled

Type mapping:

Segment Return type
`[param]` `Promise`
`[...param]` `Promise<string[]>`
`[[...param]]` `Promise<string[] | undefined>`

If the same param name appears across multiple root layouts with different segment types, the most permissive type is used.

Example output for `app/[lang]/[locale]/layout.tsx` + `app/catch-all/[...path]/layout.tsx`:
```ts
declare module 'next/root-params' {
export function lang(): Promise
export function locale(): Promise
export function path(): Promise<string[]>
}
```

Test plan

  • `pnpm testonly test/e2e/app-dir/app-root-params-getters/typecheck.test.ts` — new test verifying generated types for simple and multiple-roots fixtures
  • `pnpm testonly test/e2e/app-dir/app-root-params-getters/simple.test.ts` — existing runtime tests pass
  • `pnpm testonly test/e2e/app-dir/app-root-params-getters/multiple-roots.test.ts` — existing runtime tests pass

Copilot AI review requested due to automatic review settings February 18, 2026 17:23
@nextjs-bot
Copy link
Collaborator

nextjs-bot commented Feb 18, 2026

Allow CI Workflow Run

  • approve CI run for commit: 0e17a84

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

Collects root layout dynamic segments during type generation and
emits a typed `declare module 'next/root-params'` declaration to
.next/types/root-params.d.ts. The declaration is included via the
routes.d.ts entry file when experimental.rootParams (or cacheComponents)
is enabled.

- simple [param] → Promise<string>
- [...param] (catch-all) → Promise<string[]>
- [[...param]] (optional catch-all) → Promise<string[] | undefined>
- multiple root layouts with the same param name use the most permissive type

Also adds a typecheck test that builds the existing root-params fixtures
and verifies the generated types match expected return types.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements type generation for the next/root-params module, automatically creating typed getter functions based on dynamic segments found in root layouts. Root layouts are identified as the shallowest layout in each branch of the app directory tree. The generated types are written to .next/types/root-params.d.ts during both next build and next dev when experimental.rootParams or cacheComponents is enabled.

Changes:

  • Adds collectRootParamsFromLayouts function to identify root layouts and extract their dynamic segments
  • Implements generateRootParamsTypes and writeRootParamsTypes utilities to create TypeScript declarations
  • Integrates root params type generation into both build and dev workflows
  • Replaces magic number 0xfffffffe with INFINITE_CACHE constant in cache-life utilities (from PR #90146)

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/e2e/app-dir/app-root-params-getters/typecheck.test.ts Adds tests verifying generated types for simple and multiple-roots fixtures
test/e2e/app-dir/app-root-params-getters/fixtures/simple/type-tests.ts TypeScript test file to validate generated type signatures
packages/next/src/server/lib/router-utils/setup-dev-bundler.ts Integrates root params type generation in dev mode
packages/next/src/server/lib/router-utils/route-types-utils.ts Adds root layout detection, param collection, and file generation logic
packages/next/src/server/lib/router-utils/root-params-type-utils.ts Implements type generation and file writing for root params
packages/next/src/server/lib/router-utils/cache-life-type-utils.ts Replaces magic number with INFINITE_CACHE constant
packages/next/src/build/index.ts Integrates root params and cache-life type generation in build mode

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +197 to +202
const kind: RootParamKind =
param.paramType === 'optional-catchall'
? 'optional-catchall'
: param.paramType === 'catchall'
? 'catchall'
: 'dynamic'
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current logic doesn't correctly handle intercepted route param types. When getSegmentParam returns param types like 'catchall-intercepted-(.)' or 'dynamic-intercepted-(...)', the direct equality checks will fail, causing intercepted catchall routes to be incorrectly classified as 'dynamic'.

Instead, import and use the existing getParamProperties helper from '../../../shared/lib/router/utils/get-segment-param':

Add to imports:

import { getSegmentParam, getParamProperties } from '../../../shared/lib/router/utils/get-segment-param'

Replace lines 197-202 with:

const { repeat, optional } = getParamProperties(param.paramType)
const kind: RootParamKind =
  repeat && optional
    ? 'optional-catchall'
    : repeat
      ? 'catchall'
      : 'dynamic'

This handles all param type variants including intercepted routes.

Copilot uses AI. Check for mistakes.
Comment on lines +536 to +541
export function writeRootParamsTypesFile(
manifest: RouteTypesManifest,
filePath: string
) {
writeRootParamsTypes(manifest.rootParams, filePath)
}
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, this function should be declared as async to match the pattern used by writeCacheLifeTypesFile at line 529. Even though the underlying writeRootParamsTypes call is synchronous, using async maintains consistency with other file-writing functions in this module and allows for future async operations if needed.

Copilot uses AI. Check for mistakes.
'types',
'root-params.d.ts'
)
writeRootParamsTypesFile(
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing await before writeRootParamsTypesFile. Even though the function is currently synchronous, it should be called with await for consistency with line 1425 where writeCacheLifeTypesFile is awaited, and to prevent issues if the function becomes async in the future.

Suggested change
writeRootParamsTypesFile(
await writeRootParamsTypesFile(

Copilot uses AI. Check for mistakes.
@bgub bgub force-pushed the feat/root-params-types branch from 7533724 to 0e17a84 Compare February 18, 2026 17:30
) {
if (!rootParams || rootParams.size === 0) {
return
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When experimental.rootParams is enabled but no root layouts have dynamic segments, routes.d.ts imports root-params.d.ts which is never written, causing a TypeScript compilation error.

Fix on Vercel

@bgub bgub marked this pull request as draft February 18, 2026 18:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments