Add variadic path parameters support#7735
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds non-terminal variadic path parameters ( ChangesVariadic path parameters feature
Estimated code review effort: 4 (Complex) | ~75 minutes Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
packages/router-generator/src/validate-route-params.ts (2)
53-70: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRegex update looks correct; minor stale comment.
The updated
bracePatterncorrectly captures the...$prefix alongside-$/$, and since onlymatch[2](the param name) is used here, validity checking is unaffected by prefix type. One nit: the comment at Line 62 (// This is a wildcard {$} or {-$}, skip) doesn't mention that a nameless{...$}is now also silently skipped via the same branch.📝 Suggested comment tweak
- if (!paramName) { - // This is a wildcard {$} or {-$}, skip + if (!paramName) { + // This is a wildcard {$}, {-$}, or {...$}, skip continue }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/router-generator/src/validate-route-params.ts` around lines 53 - 70, The brace-pattern handling in validate-route-params.ts is correct, but the stale inline comment in the wildcard branch no longer reflects the supported patterns. Update the comment near the paramName guard in validateRouteParams to note that nameless {...$} is also skipped, alongside {$} and {-$}, so the documentation matches the current bracePattern behavior.
55-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffDuplicated brace-pattern regex across two packages.
The same
bracePatternregex (and near-identicalextractParamsFromSegmentlogic) now exists in both this file andpackages/eslint-plugin-router/src/rules/route-param-names/route-param-names.utils.ts. Any future change to the variadic/optional param syntax will need to be kept in sync manually across both packages, risking drift.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/router-generator/src/validate-route-params.ts` at line 55, The bracePattern regex and extractParamsFromSegment logic are duplicated between validate-route-params and route-param-names.utils, so update this code to use a shared helper instead of maintaining two copies. Move the regex and segment parsing behavior into a common utility that both packages can import, then have validate-route-params and the route-param-names rule consume that shared implementation to keep variadic/optional param syntax changes in one place.packages/eslint-plugin-router/src/rules/route-param-names/route-param-names.utils.ts (2)
25-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd test coverage for
{...$paramName}extraction.Existing tests (per provided context in
route-param-names.utils.test.ts) cover$param,{$param}, and{-$param}but no case is shown for the new{...$paramName}variadic form, including itsisOptional: falsebehavior andfullParamvalue. Since this file is the entry point for eslint recognizing the new syntax, add explicit tests for it.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/eslint-plugin-router/src/rules/route-param-names/route-param-names.utils.ts` around lines 25 - 79, Add test coverage in route-param-names.utils.test.ts for the new variadic `{...$paramName}` form handled by extractParamsFromSegment in route-param-names.utils.ts. Create explicit assertions that the parser returns the extracted param with the correct paramName, fullParam value, and isOptional: false, alongside the existing `$param`, `{$param}`, and `{-$param}` cases.
3-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueStale doc comments after adding variadic prefix support.
The
fullParamJSDoc (Line 4) and the inline comment at Line 60 still only reference$userId/-$optionaland"$" or "-$"respectively, without mentioning the new...$paramNamevariadic form now handled by the regex.📝 Suggested comment updates
- /** The full param string including $ prefix (e.g., "$userId", "-$optional") */ + /** The full param string including $ prefix (e.g., "$userId", "-$optional", "...$items") */- const prefix = match[1] // "$" or "-$" + const prefix = match[1] // "$", "-$", or "...$"Also applies to: 54-61
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/eslint-plugin-router/src/rules/route-param-names/route-param-names.utils.ts` around lines 3 - 12, Update the stale documentation in ExtractedParam and the related parsing comments so they reflect the new variadic parameter form handled by the route param regex. In route-param-names.utils.ts, revise the JSDoc for fullParam and the inline comments around the regex logic to mention ...$paramName alongside $userId and -$optional, keeping the descriptions aligned with the actual behavior in the route param parsing helpers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/router/guide/path-params.md`:
- Line 811: The path-params guide wording is too narrow about variadic segment
restrictions. Update the sentence in the path-params documentation so that the
rule around variadics and splats matches the behavior in the router tests: a
variadic must be separated from a wildcard/splat by a static segment, not just
not “directly followed” by one. Keep the rest of the variadic-segment
explanation in sync with the behavior described by the route parsing rules.
In `@docs/router/routing/routing-concepts.md`:
- Around line 376-378: Clarify the route ranking sentence in the variadic
parameter section: the current wording says variadic routes are below optional
routes, but the example in the same paragraph uses both a dynamic route and an
optional route. Update the text around the variadic parameter precedence note so
it either says “dynamic and optional” or splits the example to match the actual
routes shown, keeping the terminology consistent with the Path Params/variadic
route documentation.
---
Nitpick comments:
In
`@packages/eslint-plugin-router/src/rules/route-param-names/route-param-names.utils.ts`:
- Around line 25-79: Add test coverage in route-param-names.utils.test.ts for
the new variadic `{...$paramName}` form handled by extractParamsFromSegment in
route-param-names.utils.ts. Create explicit assertions that the parser returns
the extracted param with the correct paramName, fullParam value, and isOptional:
false, alongside the existing `$param`, `{$param}`, and `{-$param}` cases.
- Around line 3-12: Update the stale documentation in ExtractedParam and the
related parsing comments so they reflect the new variadic parameter form handled
by the route param regex. In route-param-names.utils.ts, revise the JSDoc for
fullParam and the inline comments around the regex logic to mention
...$paramName alongside $userId and -$optional, keeping the descriptions aligned
with the actual behavior in the route param parsing helpers.
In `@packages/router-generator/src/validate-route-params.ts`:
- Around line 53-70: The brace-pattern handling in validate-route-params.ts is
correct, but the stale inline comment in the wildcard branch no longer reflects
the supported patterns. Update the comment near the paramName guard in
validateRouteParams to note that nameless {...$} is also skipped, alongside {$}
and {-$}, so the documentation matches the current bracePattern behavior.
- Line 55: The bracePattern regex and extractParamsFromSegment logic are
duplicated between validate-route-params and route-param-names.utils, so update
this code to use a shared helper instead of maintaining two copies. Move the
regex and segment parsing behavior into a common utility that both packages can
import, then have validate-route-params and the route-param-names rule consume
that shared implementation to keep variadic/optional param syntax changes in one
place.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b7d5705a-c158-49f9-b01f-c0b7bf961c9c
📒 Files selected for processing (14)
.changeset/variadic-path-params.mddocs/router/guide/path-params.mddocs/router/routing/route-matching.mddocs/router/routing/routing-concepts.mddocs/start/framework/react/migrate-from-next-js.mdpackages/eslint-plugin-router/src/rules/route-param-names/route-param-names.utils.tspackages/router-core/src/link.tspackages/router-core/src/new-process-route-tree.tspackages/router-core/src/path.tspackages/router-core/src/route.tspackages/router-core/tests/new-process-route-tree.test.tspackages/router-core/tests/variadic-path-params.test-d.tspackages/router-core/tests/variadic-path-params.test.tspackages/router-generator/src/validate-route-params.ts
82c0029 to
4efb2f5
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/router/guide/path-params.md`:
- Around line 724-726: Update the opening sentence in the Variadic Path
Parameters section so it applies only to regular path params, not variadic ones;
the current blanket statement conflicts with the new `{...$paramName}` behavior.
Adjust the wording near the introduction of this section in the path-params
guide, keeping the rest of the explanation about variadic matching unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: df6478b5-acbf-46c7-8a56-891844f12769
📒 Files selected for processing (15)
.changeset/variadic-path-params.mddocs/router/guide/path-params.mddocs/router/routing/route-matching.mddocs/router/routing/routing-concepts.mddocs/start/framework/react/migrate-from-next-js.mdpackages/eslint-plugin-router/src/__tests__/route-param-names.utils.test.tspackages/eslint-plugin-router/src/rules/route-param-names/route-param-names.utils.tspackages/router-core/src/link.tspackages/router-core/src/new-process-route-tree.tspackages/router-core/src/path.tspackages/router-core/src/route.tspackages/router-core/tests/new-process-route-tree.test.tspackages/router-core/tests/variadic-path-params.test-d.tspackages/router-core/tests/variadic-path-params.test.tspackages/router-generator/src/validate-route-params.ts
✅ Files skipped from review due to trivial changes (3)
- docs/start/framework/react/migrate-from-next-js.md
- docs/router/routing/route-matching.md
- .changeset/variadic-path-params.md
🚧 Files skipped from review as they are similar to previous changes (7)
- packages/router-core/src/path.ts
- packages/router-core/tests/new-process-route-tree.test.ts
- packages/router-core/src/route.ts
- packages/router-core/tests/variadic-path-params.test-d.ts
- packages/router-core/src/link.ts
- packages/router-core/src/new-process-route-tree.ts
- packages/router-core/tests/variadic-path-params.test.ts
4efb2f5 to
865a313
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/router/guide/path-params.md`:
- Around line 735-775: The repeated framework headings in the path params
examples need to be renamed to avoid duplicate Markdown headings (MD024). Update
the second pair of headings in this section so they are distinct from the
earlier “React” and “Solid” headings, such as using “React example” and “Solid
example,” while keeping the surrounding examples and symbols like
createFileRoute, Route, and FileComponent unchanged.
In `@docs/router/routing/routing-concepts.md`:
- Around line 334-372: The repeated framework headings in the routing concepts
markdown are triggering the duplicate-heading rule because the same section
labels are used earlier in the document. Update the second pair of headings in
this React/Solid example block to distinct text, and keep the example code
blocks under those renamed headings so the markdown remains unique and
searchable.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a5e20e00-1732-4dc7-b416-bf78b766b7a9
📒 Files selected for processing (4)
docs/router/guide/path-params.mddocs/router/routing/route-matching.mddocs/router/routing/routing-concepts.mddocs/start/framework/react/migrate-from-next-js.md
✅ Files skipped from review due to trivial changes (2)
- docs/router/routing/route-matching.md
- docs/start/framework/react/migrate-from-next-js.md
|
View your CI Pipeline Execution ↗ for commit 865a313
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Nx Cloud has identified a possible root cause for your failed CI:
We identified that the tanstack-start-example-rscs:build failure is unrelated to this PR — it stems from a broken nitro-nightly dependency where ./runtime/meta is not a defined export, causing the Vite config to fail on load. We confirmed the same failure exists independently in branch 7732, indicating a pre-existing environment issue across the repository.
No code changes were suggested for this issue.
You can trigger a rerun by pushing an empty commit:
git commit --allow-empty -m "chore: trigger rerun"
git push
🎓 Learn more about Self-Healing CI on nx.dev
Merging this PR will improve performance by 22.1%
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing |
3c238cd to
e290b5c
Compare
Non-terminal, named, multi-segment path parameters: {...$param} matches
zero or more URL segments mid-path, binds them as Array<string> with
per-segment decoding, and allows child routes to continue after it,
unlike the terminal $ splat. Matching is non-greedy, resolved by the
existing backtracking matcher, and ranks below optional params and
above the terminal wildcard. A route path supports up to three
variadics; consecutive unbounded segments (variadic-variadic or
variadic-splat) must be separated by a static segment, since an
unanchored boundary would leave the first structurally empty. No
prefix/suffix. Per-variadic consumption counts pack into 17-bit lanes
of one float64-exact integer on the match frame (at most 131071
segments per variadic; longer URLs fail to match rather than corrupt
counts). Type layer resolves variadic params as Array<string> via a
fourth ParsePathParams bucket. The generator's file-route tokenizer
keeps the dots of a variadic token instead of treating them as
flat-route separators, and generator and eslint-plugin param-name
validation accept the new token.
Adds the variadic section to the path-params guide and routing concepts, includes optional and variadic parameters in the route matching order (optional parameters were missing from it), and maps Next.js optional catch-all segments to variadic parameters in the migration guide.
e290b5c to
860c45c
Compare
Hi team,
This PR is both a proposal and its implementation. I am open to any feedback or change of direction.
Summary
This PR adds a new path-segment kind, spelled
{...$param}, which is a named, non-terminal, multi-segment parameter:It matches zero or more URL segments in the middle of a path, binds them as
Array<string>(per-segment decoded), and, unlike the existing$splat, allows routes to continue after it. A route path may contain up to three variadic segments if consecutive ones are separated by a static segment.Problem
TanStack Router's grammar today has exactly one multi-segment construct: the
$splat, and it is greedy-terminal. It consumes the remainder of the URL and cannot have children.That makes an entire family of URL schemes (paths where a variable-depth hierarchy sits between fixed parts of the URL, like
/files/{path…}/previewor/$bucket/{...$folders}/$file/versions/$versionId) inexpressible as typed routes.The only workaround inside the current grammar is unrolling depth into repeated optionals. E.g.
Proposed syntax
{...$param}joins the existing braced-segment family ({-$param}optional,{prefix{$id}suffix},{$}), and...reads as JS spread, familiar from Next.js's[...slug].The value is
Array<string>rather than a joined string because a joined string cannot distinguish an encoded/(%2F) inside a segment from a segment separator. An array preserves the distinction, and interpolation re-encodes per element.Matching semantics
static > required param > optional param > variadic > splat.{...$param}previously fell through to the tokenizer's literal-static fallback, so only a route deliberately matching that literal text would change behavior. No existing app's matching order can change.Examples
/$bucket/{...$folders}/$file/media/kyoto.jpg{bucket:'media', folders:[], file:'kyoto.jpg'}/$bucket/{...$folders}/$file/media/photos/2026/kyoto.jpg{folders:['photos','2026'], file:'kyoto.jpg'}/$bucket/{...$folders}/$file/versions/$id/media/photos/kyoto.jpg/versions/42{folders:['photos'], file:'kyoto.jpg', id:'42'}/$bucket/{...$folders}/$fileand/$bucket/settings/media/settingssettingsroute/files/{...$path}/preview/files/preview{path:[]}Usage with
Linkparams.folders: ['a','b']->/a/b(each elementencodeURIComponent-encoded, joined with/).[]orundefined-> the segment is omitted entirely (no double slashes).isMissingParams.Summary by CodeRabbit
{...$param}to match zero or more middle segments and capture decoded values asArray<string>.