feat(router-core): validate params while matching [WIP]
[!WARNING] UNTESTED, DO NOT MERGE
Use the params.parse option (https://tanstack.com/router/latest/docs/framework/react/api/router/RouteOptionsType#paramsparse-method) to validate path params while matching a route. This allows us to look for another match if the validation fails.
Pros: more flexible routing. Any $param can now become a regex route segment (or any validation you like).
Cons: routes with a params.parse method cannot share the same node as other routes of otherwise the same shape. If used frequently, this increases branching, which increases the number of branches we need to explore to find a match.
Some details:
- routes w/
params.parsehave priority over routes without (all else being equal) -
extractParamsis now "resumable" so we can call it at several point while matching without repeating work (at the expense of allocating more objects)
TODO:
- [ ] find a way to store and return the parsed params from
params.parseto avoid duplicate work after matching - [ ] when several
params.parsefunction are on the way, should the 2nd receive the output of the 1st? Or do they both receive the raw Record<string,string>? - [ ] error during
params.parseshould be preserved to be set in the store or thrown ifopts?.throwOnError - [ ] why is there
routeParamsandstrictParams? - [ ] support deprecated
options.parseParamsin addition tooptions.params.parse - [ ] should
notFoundandredirecterrors still be respected when we're trying to "continue matching" despite the parsing error?
I think each parse function receives the output of the previous one, see below. But I also think this means we can't decode it to something not stringifyable by String() (i.e. objects) because this partially parsed object will be used in interpolatePath to generate part of the matchId
https://github.com/TanStack/router/blob/d9e403b8b32867f9f84e33120467e0d1bcc13a11/packages/router-core/src/router.ts#L1393-L1417
[!IMPORTANT]
Review skipped
Draft detected.
Please check the settings in the CodeRabbit UI or the
.coderabbit.yamlfile in this repository. To trigger a single review, invoke the@coderabbitai reviewcommand.You can disable this status message by setting the
reviews.review_statustofalsein the CodeRabbit configuration file.
Walkthrough
This change extends the routing engine to support route-level custom parameter parsing. Route options now accept a parse function that is stored on segment nodes during tree construction. During route matching, parameters are extracted and parsed early in the traversal; parse errors prune the current path from consideration.
Changes
| Cohort / File(s) | Summary |
|---|---|
Route-level parameter parsing support packages/router-core/src/new-process-route-tree.ts |
Added parse field to SegmentNode<T> type and RouteLike.options.params to store and apply custom parameter parsing functions. Modified extractParams to return [params, state] tuple. Updated getNodeMatch to apply node.parse when present. Enhanced MatchStackFrame with extract and params fields to thread parsing state through the stack. Adjusted sortDynamic to prioritize nodes with parse functions. Updated all node creation functions (createStaticNode, createDynamicNode, etc.) to initialize parse: null. Modified matching paths (static, dynamic, optional, wildcard) to propagate parse state consistently. |
Sequence Diagram
sequenceDiagram
participant Router as Route Matching Engine
participant Tree as Segment Tree
participant Node as Segment Node
participant Parse as Parse Function
participant Result as Match Result
Router->>Tree: Start traversal
Router->>Node: Check for parse function
alt parse function exists
Node-->>Router: parse function found
Router->>Router: Extract params early
Router->>Parse: Apply parse(params)
alt parse succeeds
Parse-->>Router: Parsed params
Router->>Router: Attach to frame
Router->>Result: Continue matching with parsed params
else parse throws error
Parse-->>Router: Error
Router->>Router: Prune current path
Router->>Result: Try alternate routes
end
else no parse function
Node-->>Router: parse = null
Router->>Result: Continue standard matching
end
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45 minutes
- Core matching logic refactoring: Dense changes to the route matching pipeline affecting parameter extraction, frame propagation, and error handling
-
Type signature changes: Multiple updates to
SegmentNode,MatchStackFrame, and function signatures require careful verification - Behavioral changes in error handling: Parse failures now prune paths—verify this integrates correctly with existing match fallback logic
-
Sorting logic update:
sortDynamicnow considers parse presence; review prioritization semantics to ensure correct node ordering
Possibly related PRs
- TanStack/router#5124: Related change to route params.parse handling in router-core—addresses when parsing runs and parse result reuse.
Suggested labels
package: router-core
Suggested reviewers
- schiller-manuel
Poem
🐰 A rabbit hops through routes with grace,
Parse functions now find their place,
Parameters bend to custom will,
Errors prune with graceful skill,
The tree grows wise, the matches thrill! ✨
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | The title 'feat(router-core): validate params while matching [WIP]' accurately describes the main change: introducing parameter validation during route matching through route-level param parsing hooks. |
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
Comment @coderabbitai help to get the list of available commands and usage tips.
View your CI Pipeline Execution ↗ for commit 7f821bfb2f25536919d0ae0e86e00c580e749eea
| Command | Status | Duration | Result |
|---|---|---|---|
nx affected --targets=test:eslint,test:unit,tes... |
✅ Succeeded | 1m 15s | View ↗ |
nx run-many --target=build --exclude=examples/*... |
✅ Succeeded | 1s | View ↗ |
☁️ Nx Cloud last updated this comment at 2025-12-22 20:43:02 UTC
More templates
- tanstack-router-react-example-authenticated-routes
- tanstack-router-react-example-authenticated-routes-firebase
- tanstack-router-react-example-basic
- tanstack-router-react-example-basic-default-search-params
- tanstack-router-react-example-basic-devtools-panel
- tanstack-router-react-example-basic-file-based
- tanstack-router-react-example-basic-non-nested-devtools
- tanstack-router-react-example-react-query
- tanstack-router-react-example-basic-react-query-file-based
- tanstack-router-react-example-basic-ssr-file-based
- tanstack-router-react-example-basic-ssr-streaming-file-based
- tanstack-router-react-example-basic-virtual-file-based
- tanstack-router-react-example-basic-virtual-inside-file-based
- tanstack-router-react-example-deferred-data
- tanstack-router-i18n-paraglide
- tanstack-router-react-example-kitchen-sink
- tanstack-router-react-example-kitchen-sink-file-based
- tanstack-router-react-example-kitchen-sink-react-query
- tanstack-router-react-example-kitchen-sink-react-query-file-based
- tanstack-router-react-example-large-file-based
- tanstack-router-react-example-location-masking
- tanstack-router-react-example-navigation-blocking
- tanstack-router-react-example-quickstart
- tanstack-router-react-example-quickstart-esbuild-file-based
- tanstack-router-react-example-quickstart-file-based
- tanstack-router-react-example-quickstart-rspack-file-based
- tanstack-router-react-example-quickstart-webpack-file-based
- router-monorepo-react-query
- router-mono-simple
- router-mono-simple-lazy
- tanstack-router-react-example-scroll-restoration
- tanstack-search-validator-adapters
- tanstack-start-example-bare
- tanstack-start-example-basic
- tanstack-start-example-basic-auth
- tanstack-router-react-example-basic-authjs
- tanstack-start-example-basic-cloudflare
- tanstack-start-example-basic-react-query
- tanstack-start-example-basic-rsc
- tanstack-start-example-basic-static
- tanstack-start-bun-hosting
- tanstack-start-example-clerk-basic
- tanstack-start-example-convex-trellaux
- tanstack-start-example-counter
- tanstack-start-i18n-paraglide
- tanstack-start-example-large
- tanstack-start-example-material-ui
- tanstack-start-streaming-data-from-server-functions
- tanstack-start-example-supabase-basic
- tanstack-start-tailwind-v4
- tanstack-start-example-trellaux
- tanstack-start-example-workos
- tanstack-router-react-example-view-transitions
- tanstack-router-react-example-with-framer-motion
- tanstack-router-react-example-with-trpc
- tanstack-router-react-example-with-trpc-react-query
- tanstack-router-solid-example-authenticated-routes
- tanstack-router-solid-example-authenticated-routes-firebase
- tanstack-router-solid-example-basic
- tanstack-router-solid-example-basic-default-search-params
- tanstack-router-solid-example-basic-devtools-panel
- tanstack-router-solid-example-basic-file-based
- tanstack-router-solid-example-basic-non-nested-devtools
- tanstack-router-solid-example-basic-solid-query
- tanstack-router-solid-example-basic-solid-query-file-based
- tanstack-router-solid-example-basic-ssr-file-based
- tanstack-router-solid-example-basic-ssr-streaming-file-based
- tanstack-router-solid-example-basic-virtual-file-based
- tanstack-router-solid-example-basic-virtual-inside-file-based
- tanstack-router-solid-example-deferred-data
- tanstack-router-solid-i18n-paraglide
- tanstack-router-solid-example-kitchen-sink
- tanstack-router-solid-example-kitchen-sink-file-based
- tanstack-router-solid-example-kitchen-sink-solid-query
- tanstack-router-solid-example-kitchen-sink-solid-query-file-based
- tanstack-router-solid-example-large-file-based
- tanstack-router-solid-example-location-masking
- tanstack-router-solid-example-navigation-blocking
- tanstack-router-solid-example-quickstart
- tanstack-router-solid-example-quickstart-esbuild-file-based
- tanstack-router-solid-example-quickstart-file-based
- tanstack-router-solid-example-quickstart-rspack-file-based
- tanstack-router-solid-example-quickstart-webpack-file-based
- @tanstack/router-solid-mono-simple
- router-solid-mono-simple-lazy
- router-solid-monorepo-solid-query
- tanstack-router-solid-example-scroll-restoration
- tanstack-solid-router-search-validator-adapters
- tanstack-solid-start-example-basic
- tanstack-solid-start-example-basic-auth
- tanstack-solid-start-example-basic-authjs
- tanstack-solid-start-example-basic-cloudflare
- tanstack-solid-start-example-basic-netlify
- tanstack-solid-start-example-basic-nitro
- tanstack-start-example-basic-solid-query
- tanstack-solid-start-example-basic-static
- tanstack-solid-start-bun-hosting
- tanstack-solid-start-example-convex-better-auth
- tanstack-solid-start-example-counter
- tanstack-solid-start-i18n-paraglide
- tanstack-solid-start-example-large
- tanstack-solid-start-streaming-data-from-server-functions
- tanstack-solid-start-example-supabase-basic
- tanstack-solid-start-tailwind-v4
- tanstack-router-solid-example-view-transitions
- tanstack-router-solid-example-with-framer-motion
- tanstack-router-solid-example-with-trpc
- tanstack-router-vue-example-basic-jsx
- tanstack-router-vue-example-basic-file-based-jsx
- tanstack-router-vue-example-basic-file-based-sfc
@tanstack/arktype-adapter
npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@5936
@tanstack/directive-functions-plugin
npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@5936
@tanstack/eslint-plugin-router
npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@5936
@tanstack/history
npm i https://pkg.pr.new/TanStack/router/@tanstack/history@5936
@tanstack/nitro-v2-vite-plugin
npm i https://pkg.pr.new/TanStack/router/@tanstack/nitro-v2-vite-plugin@5936
@tanstack/react-router
npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@5936
@tanstack/react-router-devtools
npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@5936
@tanstack/react-router-ssr-query
npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@5936
@tanstack/react-start
npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@5936
@tanstack/react-start-client
npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@5936
@tanstack/react-start-server
npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@5936
@tanstack/router-cli
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@5936
@tanstack/router-core
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@5936
@tanstack/router-devtools
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@5936
@tanstack/router-devtools-core
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@5936
@tanstack/router-generator
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@5936
@tanstack/router-plugin
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@5936
@tanstack/router-ssr-query-core
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@5936
@tanstack/router-utils
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@5936
@tanstack/router-vite-plugin
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@5936
@tanstack/server-functions-plugin
npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@5936
@tanstack/solid-router
npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@5936
@tanstack/solid-router-devtools
npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@5936
@tanstack/solid-router-ssr-query
npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-ssr-query@5936
@tanstack/solid-start
npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@5936
@tanstack/solid-start-client
npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@5936
@tanstack/solid-start-server
npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@5936
@tanstack/start-client-core
npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@5936
@tanstack/start-fn-stubs
npm i https://pkg.pr.new/TanStack/router/@tanstack/start-fn-stubs@5936
@tanstack/start-plugin-core
npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@5936
@tanstack/start-server-core
npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@5936
@tanstack/start-static-server-functions
npm i https://pkg.pr.new/TanStack/router/@tanstack/start-static-server-functions@5936
@tanstack/start-storage-context
npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@5936
@tanstack/valibot-adapter
npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@5936
@tanstack/virtual-file-routes
npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@5936
@tanstack/vue-router
npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router@5936
@tanstack/vue-router-devtools
npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router-devtools@5936
@tanstack/vue-router-ssr-query
npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router-ssr-query@5936
@tanstack/vue-start
npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start@5936
@tanstack/vue-start-client
npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-client@5936
@tanstack/vue-start-server
npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-server@5936
@tanstack/zod-adapter
npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@5936
commit: 7f821bf
@coderabbitai review
✅ Actions performed
Review triggered.
Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.