Skip to content

Conversation

@Sheraff
Copy link
Contributor

@Sheraff Sheraff commented Nov 21, 2025

Warning

UNTESTED, DO NOT MERGE

summary

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.parse have priority over routes without (all else being equal)
  • extractParams is now "resumable" so we can call it at several point while matching without repeating work (at the expense of allocating more objects)
  • we now have index and pathless nodes in the segment tree so that previously "invisible" segments can carry "route matching metadata", i.e. in this case params.parse and skipRouteOnParseError

context

We're in the process of implementing a new functionality in tanstack/router: the skipRouteOnParseError option on a route will force the route matching algorithm to execute the params.parse option of that route before proceeding. If that function throws, then the route cannot be matched, and we keep iterating to find a match. If the function does not throw, proceed as usual.

The params.parse function has a 2nd role of also transforming a Record<string, string> of params directly extracted from the string pathname into a Record<string, unknown> of objects of arbitrary shape. This used to be done in matchRoutesInternal, but now that we also need params.parse inside getNodeMatch, we should take care of avoiding duplicate work.

next steps

The core of the logic is there. What's missing is

  • tests
  • what should be returned by findRouteMatch so that matchRoutesInternal doesn't have to re-parse the params

Right now, I'm just accumulating all parsed params in an object, but I think we might need the params in several different shapes

  • raw string params for the entire path
    • => to be used by interpolatePath to create match ids
  • final params object to return in application code
    • => right now I'm only running the params.parse on the routes that have skipRouteOnParseError, so we should find a way to run the rest (ideally after matching to avoid unnecessary work, and ideally without having to re-run the ones we already did to avoid duplicate work)
    • => I haven't figured out yet if it's ok to have one big object of params for the entire path, or if each route really needs to only have the params "up to this route" (i.e. excluding children)

TODO:

  • support deprecated options.parseParams in addition to options.params.parse

Summary by CodeRabbit

  • New Features

    • Route-level parameter parsing: routes can provide custom parse handlers for path params.
    • New route option to control behavior when param parsing fails: skipRouteOnParseError (with optional priority).
  • Documentation

    • Added guide on validating path parameters during matching with examples and guidance.
    • Expanded docs for allowed characters and configuration for path params.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 21, 2025

📝 Walkthrough

Walkthrough

Adds per-route parameter parsing: RouteLike.options.params.parse is stored on segment leaf nodes and invoked earlier during matching to produce parsed params on match frames; parse failures prune the current path and matching proceeds to alternatives. Also documents an experimental skipRouteOnParseError option.

Changes

Cohort / File(s) Summary
Router core — segment tree & matching
packages/router-core/src/new-process-route-tree.ts
Add `parse: null
Router types & route options
packages/router-core/src/route.ts
Add skipRouteOnParseError?: { params?: boolean; priority?: number } to UpdatableRouteOptions; update JSDoc/comments for related fields.
Documentation — API & guides
docs/router/framework/react/api/router/RouteOptionsType.md, docs/router/framework/react/guide/path-params.md
Document experimental skipRouteOnParseError and show usage, examples, priority behavior, and guidance for path param validation and allowed characters.

Sequence Diagram

sequenceDiagram
    participant Orchestrator as Router (match orchestrator)
    participant Tree as Segment Tree
    participant Node as Segment Node
    participant Parser as Route params.parse
    participant Result as Match Result

    Orchestrator->>Tree: begin traversal for path
    Tree->>Node: examine candidate node
    alt node has parse
        Node-->>Orchestrator: parse present on leaf
        Orchestrator->>Orchestrator: run extractParams (early) -> [params,state]
        Orchestrator->>Parser: call parse(extractedParams)
        alt parse succeeds
            Parser-->>Orchestrator: parsed params
            Orchestrator->>Orchestrator: attach params to frame
            Orchestrator->>Tree: continue traversal with parsed params
            Tree-->>Result: produce match
        else parse throws
            Parser-->>Orchestrator: error
            Orchestrator->>Orchestrator: prune this path
            Orchestrator->>Tree: try alternate nodes/routes
        end
    else no parse
        Node-->>Orchestrator: no parse
        Orchestrator->>Tree: continue standard matching
        Tree-->>Result: produce match
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • schiller-manuel
  • nlynzaad

Poem

🐰 I hopped along the segment line,
Parsers nestle where leaf nodes shine,
I nibble errors, skip the path,
Match hops on — no aftermath,
A tiny hop, a tidy route, hooray!

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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(router-core): validate params while matching [WIP]' accurately describes the main feature being implemented—validating route parameters during the matching phase—which aligns with the substantial changes across route parsing, matching logic, and documentation.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-router-core-validate-params-while-matching

Comment @coderabbitai help to get the list of available commands and usage tips.

@Sheraff Sheraff changed the title feat(router-core): validate params while matching feat(router-core): validate params while matching [WIP] Nov 21, 2025
@nx-cloud
Copy link

nx-cloud bot commented Nov 21, 2025

View your CI Pipeline Execution ↗ for commit 40b9428

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 18m 10s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1m 35s View ↗

☁️ Nx Cloud last updated this comment at 2025-12-24 12:23:04 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 21, 2025

More templates

@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: 40b9428

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/router-core/src/new-process-route-tree.ts (1)

917-919: Silent error handling may hinder debugging.

Parse errors are caught and silently cause the route to be skipped via continue. While this enables fallback to alternative routes, it provides no visibility into why a route was rejected, making debugging difficult for developers.

Consider adding optional logging or a debug mode that reports parse failures, especially during development.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc79c97 and d9e403b.

📒 Files selected for processing (1)
  • packages/router-core/src/new-process-route-tree.ts (21 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: FatahChan
Repo: TanStack/router PR: 5475
File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0
Timestamp: 2025-10-14T18:59:33.990Z
Learning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.
Learnt from: nlynzaad
Repo: TanStack/router PR: 5284
File: e2e/react-start/basic/server.js:50-0
Timestamp: 2025-09-28T21:41:45.233Z
Learning: In Express v5, catch-all routes must use named wildcards. Use `/*splat` to match everything except root path, or `/{*splat}` (with braces) to match including root path. The old `*` syntax is not allowed and will cause "Missing parameter name" errors. This breaking change requires explicit naming of wildcard parameters.
Learnt from: nlynzaad
Repo: TanStack/router PR: 5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.

Applied to files:

  • packages/router-core/src/new-process-route-tree.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview
🔇 Additional comments (4)
packages/router-core/src/new-process-route-tree.ts (4)

375-376: Verify parse prioritization is always desired.

The sort function unconditionally prioritizes nodes with parse functions over those without. While this makes sense for validation (try more specific routes first), it's hardcoded with no way to customize priority.

Consider whether this ordering is always appropriate, especially when multiple routes with different parse functions compete. The current tie-breaking logic (lines 377-394) would then determine order, which might be arbitrary.


236-244: Node reuse prevented when parse is present.

Routes with parse functions cannot share segment nodes, even if they're otherwise identical. This conservative approach is safe (prevents incorrect sharing of different parse functions), but increases memory usage if many routes have parse functions.

This is likely acceptable for correctness, but worth noting for performance-sensitive applications with large route trees.

Also applies to: 278-286


765-767: LGTM: Param extraction correctly resumes from saved state.

The logic properly handles incremental param extraction:

  1. Resumes from leaf.extract state if present (line 765-767)
  2. Extracts params from remaining nodes
  3. Merges previously extracted params back in (line 823)

This avoids re-extracting and re-validating params when multiple parse functions exist in the route path.

Also applies to: 823-824


534-547: LGTM: Type definitions are consistent with implementation.

The parse field is properly typed across SegmentNode, RouteLike, and MatchStackFrame. The function signature (params: Record<string, string>) => any accurately reflects that params are strings and the return type is currently unused.

@birkskyum birkskyum marked this pull request as draft November 21, 2025 23:56
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

✅ 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.

@Sheraff Sheraff marked this pull request as ready for review December 23, 2025 11:50
@Sheraff
Copy link
Contributor Author

Sheraff commented Dec 23, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 23, 2025

✅ 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.

@github-actions github-actions bot added the documentation Everything documentation related label Dec 24, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d9e403b and eed4b0b.

📒 Files selected for processing (2)
  • docs/router/framework/react/api/router/RouteOptionsType.md
  • docs/router/framework/react/guide/path-params.md
🧰 Additional context used
📓 Path-based instructions (1)
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Use relative links to docs/ folder format (e.g., ./guide/data-loading) for internal documentation references

Files:

  • docs/router/framework/react/api/router/RouteOptionsType.md
  • docs/router/framework/react/guide/path-params.md
🧠 Learnings (9)
📓 Common learnings
Learnt from: FatahChan
Repo: TanStack/router PR: 5475
File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0
Timestamp: 2025-10-14T18:59:33.990Z
Learning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.
Learnt from: nlynzaad
Repo: TanStack/router PR: 5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.
Learnt from: Sheraff
Repo: TanStack/router PR: 6171
File: packages/router-core/src/new-process-route-tree.ts:898-898
Timestamp: 2025-12-21T12:52:35.231Z
Learning: In `packages/router-core/src/new-process-route-tree.ts`, the matching logic intentionally allows paths without trailing slashes to match index routes with trailing slashes (e.g., `/a` can match `/a/` route), but not vice-versa (e.g., `/a/` cannot match `/a` layout route). This is implemented via the condition `!pathIsIndex || node.kind === SEGMENT_TYPE_INDEX` and is a deliberate design decision to provide better UX by being permissive with missing trailing slashes.
Learnt from: nlynzaad
Repo: TanStack/router PR: 5182
File: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx:3-5
Timestamp: 2025-09-22T00:56:49.237Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments (e.g., `$baz_` becomes `baz` in generated types) but should be preserved in base path segments. This is the correct behavior as of the fix in PR #5182.
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Implement type-safe routing with search params and path params
📚 Learning: 2025-12-21T12:52:35.231Z
Learnt from: Sheraff
Repo: TanStack/router PR: 6171
File: packages/router-core/src/new-process-route-tree.ts:898-898
Timestamp: 2025-12-21T12:52:35.231Z
Learning: In `packages/router-core/src/new-process-route-tree.ts`, the matching logic intentionally allows paths without trailing slashes to match index routes with trailing slashes (e.g., `/a` can match `/a/` route), but not vice-versa (e.g., `/a/` cannot match `/a` layout route). This is implemented via the condition `!pathIsIndex || node.kind === SEGMENT_TYPE_INDEX` and is a deliberate design decision to provide better UX by being permissive with missing trailing slashes.

Applied to files:

  • docs/router/framework/react/guide/path-params.md
📚 Learning: 2025-10-14T18:59:33.990Z
Learnt from: FatahChan
Repo: TanStack/router PR: 5475
File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0
Timestamp: 2025-10-14T18:59:33.990Z
Learning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.

Applied to files:

  • docs/router/framework/react/guide/path-params.md
📚 Learning: 2025-12-17T02:17:55.086Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 6120
File: packages/router-generator/src/generator.ts:654-657
Timestamp: 2025-12-17T02:17:55.086Z
Learning: In `packages/router-generator/src/generator.ts`, pathless_layout routes must receive a `path` property when they have a `cleanedPath`, even though they are non-path routes. This is necessary because child routes inherit the path from their parent, and without this property, child routes would not have the correct full path at runtime.

Applied to files:

  • docs/router/framework/react/guide/path-params.md
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Implement type-safe routing with search params and path params

Applied to files:

  • docs/router/framework/react/guide/path-params.md
📚 Learning: 2025-09-28T21:41:45.233Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5284
File: e2e/react-start/basic/server.js:50-0
Timestamp: 2025-09-28T21:41:45.233Z
Learning: In Express v5, catch-all routes must use named wildcards. Use `/*splat` to match everything except root path, or `/{*splat}` (with braces) to match including root path. The old `*` syntax is not allowed and will cause "Missing parameter name" errors. This breaking change requires explicit naming of wildcard parameters.

Applied to files:

  • docs/router/framework/react/guide/path-params.md
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{js,ts,tsx} : Implement ESLint rules for router best practices using the ESLint plugin router

Applied to files:

  • docs/router/framework/react/guide/path-params.md
📚 Learning: 2025-09-22T00:56:53.426Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.

Applied to files:

  • docs/router/framework/react/guide/path-params.md
📚 Learning: 2025-09-22T00:56:49.237Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5182
File: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx:3-5
Timestamp: 2025-09-22T00:56:49.237Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments (e.g., `$baz_` becomes `baz` in generated types) but should be preserved in base path segments. This is the correct behavior as of the fix in PR #5182.

Applied to files:

  • docs/router/framework/react/guide/path-params.md
🪛 markdownlint-cli2 (0.18.1)
docs/router/framework/react/guide/path-params.md

745-745: Blank line inside blockquote

(MD028, no-blanks-blockquote)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test
🔇 Additional comments (2)
docs/router/framework/react/api/router/RouteOptionsType.md (1)

91-110: Well-documented experimental feature with clear warnings. The new skipRouteOnParseError property is properly documented with type definition, experimental warning, and performance callout. The cross-reference to the comprehensive guide section is helpful. The documentation aligns with the related PR changes in route matching behavior.

docs/router/framework/react/guide/path-params.md (1)

741-1037: Excellent comprehensive documentation for the new feature. The "Validating Path Parameters During Matching" section is thorough and well-structured, with clear progression from basic concepts to advanced use cases. The examples covering UUIDs, dates, numeric IDs, priorities, nested routes, and optional parameters demonstrate practical real-world scenarios. The explanation of route priority and custom priority handling is particularly valuable. The integration with RouterOptions for allowed characters is practical. This documentation provides clear guidance on when and how to use this experimental feature while appropriately warning about performance implications.

Comment on lines +743 to +747
> [!WARNING]
> The `skipRouteOnParseError` option is currently **experimental** and may change in future releases.
> [!IMPORTANT]
> **Performance cost**: This feature has a **non-negligible performance cost** and should not be used indiscriminately. It creates additional branches in the route matching tree, reducing matching efficiency and requiring more route evaluations. Use it only when you genuinely need type-specific routes at the same path level.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix markdown blockquote formatting. The blank line at Line 745 breaks the blockquote formatting and triggers a linting error. Either add the > marker to continue the blockquote, or ensure the blank line is completely outside the blockquote context.

🔎 Proposed fix
 > [!WARNING]
 > The `skipRouteOnParseError` option is currently **experimental** and may change in future releases.
-
+> 
 > [!IMPORTANT]
 > **Performance cost**: This feature has a **non-negligible performance cost** and should not be used indiscriminately. It creates additional branches in the route matching tree, reducing matching efficiency and requiring more route evaluations. Use it only when you genuinely need type-specific routes at the same path level.

Alternatively, if you prefer complete separation between callouts, remove the blank line entirely and let the callouts be adjacent.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
> [!WARNING]
> The `skipRouteOnParseError` option is currently **experimental** and may change in future releases.
> [!IMPORTANT]
> **Performance cost**: This feature has a **non-negligible performance cost** and should not be used indiscriminately. It creates additional branches in the route matching tree, reducing matching efficiency and requiring more route evaluations. Use it only when you genuinely need type-specific routes at the same path level.
> [!WARNING]
> The `skipRouteOnParseError` option is currently **experimental** and may change in future releases.
>
> [!IMPORTANT]
> **Performance cost**: This feature has a **non-negligible performance cost** and should not be used indiscriminately. It creates additional branches in the route matching tree, reducing matching efficiency and requiring more route evaluations. Use it only when you genuinely need type-specific routes at the same path level.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

745-745: Blank line inside blockquote

(MD028, no-blanks-blockquote)

🤖 Prompt for AI Agents
In docs/router/framework/react/guide/path-params.md around lines 743 to 747, the
blank line at line 745 breaks the blockquote formatting and causes a lint error;
fix it by either (A) adding the leading ">" to that blank line so it remains
part of the blockquote, or (B) remove the blank line entirely (or move it
outside the blockquote) so the two callout blocks are properly separated; choose
one approach and update the file accordingly.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eed4b0b and 40b9428.

📒 Files selected for processing (1)
  • packages/router-core/src/route.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript strict mode with extensive type safety for all code

Files:

  • packages/router-core/src/route.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Implement ESLint rules for router best practices using the ESLint plugin router

Files:

  • packages/router-core/src/route.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: FatahChan
Repo: TanStack/router PR: 5475
File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0
Timestamp: 2025-10-14T18:59:33.990Z
Learning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.
Learnt from: nlynzaad
Repo: TanStack/router PR: 5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Implement type-safe routing with search params and path params
Learnt from: Sheraff
Repo: TanStack/router PR: 6171
File: packages/router-core/src/new-process-route-tree.ts:898-898
Timestamp: 2025-12-21T12:52:35.231Z
Learning: In `packages/router-core/src/new-process-route-tree.ts`, the matching logic intentionally allows paths without trailing slashes to match index routes with trailing slashes (e.g., `/a` can match `/a/` route), but not vice-versa (e.g., `/a/` cannot match `/a` layout route). This is implemented via the condition `!pathIsIndex || node.kind === SEGMENT_TYPE_INDEX` and is a deliberate design decision to provide better UX by being permissive with missing trailing slashes.
📚 Learning: 2025-12-21T12:52:35.231Z
Learnt from: Sheraff
Repo: TanStack/router PR: 6171
File: packages/router-core/src/new-process-route-tree.ts:898-898
Timestamp: 2025-12-21T12:52:35.231Z
Learning: In `packages/router-core/src/new-process-route-tree.ts`, the matching logic intentionally allows paths without trailing slashes to match index routes with trailing slashes (e.g., `/a` can match `/a/` route), but not vice-versa (e.g., `/a/` cannot match `/a` layout route). This is implemented via the condition `!pathIsIndex || node.kind === SEGMENT_TYPE_INDEX` and is a deliberate design decision to provide better UX by being permissive with missing trailing slashes.

Applied to files:

  • packages/router-core/src/route.ts
📚 Learning: 2025-12-17T02:17:55.086Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 6120
File: packages/router-generator/src/generator.ts:654-657
Timestamp: 2025-12-17T02:17:55.086Z
Learning: In `packages/router-generator/src/generator.ts`, pathless_layout routes must receive a `path` property when they have a `cleanedPath`, even though they are non-path routes. This is necessary because child routes inherit the path from their parent, and without this property, child routes would not have the correct full path at runtime.

Applied to files:

  • packages/router-core/src/route.ts
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{js,ts,tsx} : Implement ESLint rules for router best practices using the ESLint plugin router

Applied to files:

  • packages/router-core/src/route.ts
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.

Applied to files:

  • packages/router-core/src/route.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview
🔇 Additional comments (2)
packages/router-core/src/route.ts (2)

1218-1223: LGTM: Documentation improvement for caseSensitive.

Good addition of JSDoc for the existing caseSensitive option. The documentation is clear and follows the project's documentation style.


1224-1227: LGTM: Documentation improvement for wrapInSuspense.

Good addition of JSDoc for the existing wrapInSuspense option. The documentation clearly explains the behavior.

Comment on lines +1191 to +1217
/**
* Options to control route matching behavior with runtime code.
*/
skipRouteOnParseError?: {
/**
* If `true`, skip this route during matching if `params.parse` fails.
*
* Without this option, a `/$param` route could match *any* value for `param`,
* and only later during the route lifecycle would `params.parse` run and potentially
* show the `errorComponent` if validation failed.
*
* With this option enabled, the route will only match if `params.parse` succeeds.
* If it fails, the router will continue trying to match other routes, potentially
* finding a different route that works, or ultimately showing the `notFoundComponent`.
*
* @default false
*/
params?: boolean
/**
* In cases where multiple routes would need to run `params.parse` during matching
* to determine which route to pick, this priority number can be used as a tie-breaker
* for which route to try first. Higher number = higher priority.
*
* @default 0
*/
priority?: number
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check the RouteLike interface definition and its options field
rg -n "interface RouteLike" --type=ts packages/router-core/src/

# Then view the specific lines in route.ts to see the RouteLike definition
sed -n '2003,2014p' packages/router-core/src/route.ts

Repository: TanStack/router

Length of output: 342


🏁 Script executed:

# Search for skipRouteOnParseError usage throughout the codebase
rg -n "skipRouteOnParseError" --type=ts packages/router-core/src/

# Check where RouteLike is used in matching logic
rg -n "RouteLike" --type=ts -A3 packages/router-core/src/ | head -100

Repository: TanStack/router

Length of output: 7940


🏁 Script executed:

# Look for test files related to skipRouteOnParseError
find packages -name "*.test.ts" -o -name "*.spec.ts" | xargs rg -l "skipRouteOnParseError" 2>/dev/null || echo "No test files found with skipRouteOnParseError"

# Check for any @experimental tags in the route.ts file
grep -n "@experimental\|@alpha\|experimental" packages/router-core/src/route.ts | head -20

Repository: TanStack/router

Length of output: 216


🏁 Script executed:

# Check the route.ts file to see if there are other interfaces besides RouteLike
# and understand the complete type hierarchy
grep -n "interface.*Route\|type.*Route" packages/router-core/src/route.ts | head -30

# Look at the UpdatableRouteOptions interface mentioned in the review
rg -n "UpdatableRouteOptions" --type=ts packages/router-core/src/route.ts -A20

Repository: TanStack/router

Length of output: 4834


🏁 Script executed:

# Check the test file to verify if tests exist
head -50 packages/router-core/tests/skip-route-on-parse-error.test.ts

# Check how parseSegments is called and what route object is passed
rg -n "parseSegments" --type=ts packages/router-core/src/ -B2 -A2

Repository: TanStack/router

Length of output: 3893


🏁 Script executed:

# Check if there's a type declaration or wider options interface used elsewhere
rg -n "route\.options\?" --type=ts packages/router-core/src/new-process-route-tree.ts -B2 -A2 | head -50

Repository: TanStack/router

Length of output: 650


🏁 Script executed:

# Check if Route extends RouteLike or what the relationship is
rg -n "class Route\|extends.*Route\|implements.*RouteLike" --type=ts packages/router-core/src/route.ts -A3 -B1 | head -40

# Check the full test file to see test coverage quality
wc -l packages/router-core/tests/skip-route-on-parse-error.test.ts

# Check how RouteLike is used - is it for serialization/export?
rg -n "RouteLike" --type=ts packages/router-core/src/ -B2 -A2 | grep -E "export|JSON|serialize|toJSON" | head -20

Repository: TanStack/router

Length of output: 778


🏁 Script executed:

# Check the processRouteTree function to see what it accepts and returns
rg -n "function processRouteTree\|export.*processRouteTree" --type=ts packages/router-core/src/ -A10

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

# Get the processRouteTree function signature and implementation
sed -n '755,790p' packages/router-core/src/new-process-route-tree.ts

Repository: TanStack/router

Length of output: 1590


🏁 Script executed:

# Check what the test file passes as tree parameter to processRouteTree
grep -n "processRouteTree" packages/router-core/tests/skip-route-on-parse-error.test.ts -A5 | head -30

Repository: TanStack/router

Length of output: 1422


🏁 Script executed:

# Check Route class definition to understand relationship to RouteLike
sed -n '586,650p' packages/router-core/src/route.ts

Repository: TanStack/router

Length of output: 1417


🏁 Script executed:

# Search for @experimental or @alpha markers in the entire file
grep -n "@experimental\|@alpha\|experimental\|alpha" packages/router-core/src/route.ts

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

# Check if @experimental/@alpha tags are used elsewhere in the codebase
rg -n "@experimental|@alpha" --type=ts packages/router-core/src/ | head -20

Repository: TanStack/router

Length of output: 114


🏁 Script executed:

# Check Route vs RouteLike - see if they're related or separate concerns
# Look at RouteOptions to see if it extends UpdatableRouteOptions
rg -n "type RouteOptions\|interface RouteOptions" --type=ts packages/router-core/src/route.ts -A20 | head -40

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

# Check the test more thoroughly - see if it's testing real routes or just mock objects
tail -100 packages/router-core/tests/skip-route-on-parse-error.test.ts

Repository: TanStack/router

Length of output: 2824


Update RouteLike interface and add @experimental tag for type safety and consistency.

The type structure and documentation for skipRouteOnParseError are sound and well-implemented. However, two improvements are needed:

  1. RouteLike interface compatibility: Update the RouteLike interface (lines 2011-2013) to expose skipRouteOnParseError in its options field:

    options?: {
      caseSensitive?: boolean
      skipRouteOnParseError?: {
        params?: boolean
        priority?: number
      }
    }

    Currently, the code accesses route.options?.skipRouteOnParseError (new-process-route-tree.ts:192, 389) but RouteLike only declares caseSensitive. The optional chaining masks this type mismatch.

  2. Add @experimental tag: Add @experimental to the JSDoc for skipRouteOnParseError to match the pattern used elsewhere in the codebase (e.g., Matches.ts:90).

Comprehensive tests for this feature already exist in packages/router-core/tests/skip-route-on-parse-error.test.ts, covering successful parsing, error skipping with fallbacks, priority ordering, and edge cases.

🤖 Prompt for AI Agents
packages/router-core/src/route.ts around lines 1191-1217 and update RouteLike at
lines ~2011-2013: expose skipRouteOnParseError in the RouteLike.options type and
mark the JSDoc as @experimental; specifically, add skipRouteOnParseError?: {
params?: boolean; priority?: number } to RouteLike.options so calls to
route.options?.skipRouteOnParseError (new-process-route-tree.ts:192, 389) are
type-safe, and add an @experimental tag to the existing JSDoc block for
skipRouteOnParseError to match project conventions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Everything documentation related package: router-core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants