Skip to content

Conversation

@nlynzaad
Copy link
Contributor

@nlynzaad nlynzaad commented Sep 11, 2025

with changing useParams to make use of _strictParams it became apparent that _strictParams did not apply param parsing correctly. This PR resolves that problem and hence useParams now return the parsed params correctly.

This PR also adds unit tests in both react-router and solid-router to test this expected result.

A separate PR has been created to merge into alpha (#5121)

Summary by CodeRabbit

  • New Features

    • Stricter route parameter parsing and validation with support for custom parse/stringify.
    • Clearer per-route parameter error reporting, with optional throw-on-error behavior.
    • More consistent parameter values across nested routes and loaders.
  • Tests

    • Added integration tests validating useParams with nested routing, custom param parsing/stringifying, and loader data in both React and Solid routers.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 11, 2025

Walkthrough

Introduces strict per-route parameter parsing and error handling in router-core, updates match construction to use strictly parsed params, and adds new integration tests for useParams in React and Solid routers validating nested params parsing/stringifying and loader data interactions.

Changes

Cohort / File(s) Summary
Router core: strict param handling
packages/router-core/src/router.ts
Refactors match building to compute strict params from route.id interpolation; applies per-route parse to params with error capture/throw; updates matchId, _strictParams, and per-match paramsError; keeps pathname from route.fullPath.
React Router tests: useParams
packages/react-router/tests/useParams.test.tsx
Adds E2E test covering nested routes with custom param parse/stringify, loaders, and useParams({ from: postRoute.fullPath }); verifies navigation and param/loader consistency.
Solid Router tests: useParams
packages/solid-router/tests/useParams.test.tsx
Adds analogous test for Solid Router validating nested param parsing/stringifying, loaders, and useParams behavior across navigations.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant R as Router
  participant Rt as Route
  participant P as Params Parser
  participant LD as Loader

  U->>R: Navigate to URL
  R->>Rt: Match route tree
  Note right of R: Interpolate route.id to derive strict params
  R->>P: strictParse(params)
  alt parse OK
    P-->>R: parsedParams
    R->>Rt: Build match with _strictParams=parsedParams
    R->>LD: Invoke loader with parsedParams
    LD-->>R: loaderData
    R-->>U: Render with useParams/useLoaderData
  else parse throws
    P-->>R: error
    Note right of R: Create PathParamError<br/>(record or throw)
    alt throwOnError
      R-->>U: Throw error
    else record error
      R->>Rt: Build match with paramsError
      R-->>U: Render (error available on match)
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

package: react-router, package: router-core, package: solid-router

Suggested reviewers

  • schiller-manuel

Poem

I hop through paths where params hide,
From id to title, a tidy ride.
Strict little carrots parsed just right,
Loaders nibble data light.
Links go boing—category bound—
Two hops forward, posts are found.
(_/) ✅ A router well-tuned all around.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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 succinctly identifies the affected package (router-core) and the core fix (parsing _strictParams), which directly matches the PR objective to correct useParams behavior; it is concise, specific, and contains no extraneous noise.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch parse_strictParams

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

@nx-cloud
Copy link

nx-cloud bot commented Sep 11, 2025

View your CI Pipeline Execution ↗ for commit 2b966b1

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

☁️ Nx Cloud last updated this comment at 2025-09-11 07:24:34 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Sep 11, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@5120

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@5120

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@5120

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@5120

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@5120

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@5120

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@5120

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@5120

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@5120

@tanstack/react-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-plugin@5120

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@5120

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@5120

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@5120

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@5120

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@5120

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@5120

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@5120

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@5120

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@5120

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@5120

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@5120

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@5120

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@5120

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@5120

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@5120

@tanstack/solid-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-plugin@5120

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@5120

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@5120

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@5120

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@5120

@tanstack/start-server-functions-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-client@5120

@tanstack/start-server-functions-fetcher

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-fetcher@5120

@tanstack/start-server-functions-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-server@5120

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@5120

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@5120

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@5120

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@5120

commit: 2b966b1

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: 0

🧹 Nitpick comments (6)
packages/solid-router/tests/useParams.test.tsx (2)

181-191: Avoid wrapping router.load() and clicks in waitFor (can cause repeated side effects).

Call router.load() once and click directly; wait on subsequent UI assertions instead.

-  await waitFor(() => router.load())
+  await router.load()
...
-  await waitFor(() => fireEvent.click(firstCategoryLink))
+  fireEvent.click(firstCategoryLink)
...
-  await waitFor(() => fireEvent.click(allCategoryLink))
+  fireEvent.click(allCategoryLink)
...
-  await waitFor(() => fireEvent.click(secondPostLink))
+  fireEvent.click(secondPostLink)

Also applies to: 225-235


210-214: Guard against null textContent and specify radix in parseInt.

Prevents TS nullability complaints and ensures base-10 parsing.

-  let renderedPost = {
-    id: parseInt(postIdValue.textContent),
+  let renderedPost = {
+    id: parseInt(postIdValue.textContent!, 10),
     title: postTitleValue.textContent,
     category: postCategory.textContent,
   }
...
-  renderedPost = {
-    id: parseInt(postIdValue.textContent),
+  renderedPost = {
+    id: parseInt(postIdValue.textContent!, 10),
     title: postTitleValue.textContent,
     category: postCategory.textContent,
   }

Also applies to: 246-251

packages/router-core/src/router.ts (2)

1213-1218: Parse-cache missed on fullPath interpolation.

interpolatePath supports parseCache; passing it avoids re-parsing templates across navigations.

-      const { interpolatedPath } = interpolatePath({
+      const { interpolatedPath } = interpolatePath({
         path: route.fullPath,
         params: routeParams,
         decodeCharMap: this.pathParamsDecodeCharMap,
+        parseCache: this.parsePathnameCache,
       })

1227-1250: Strict per-route param parsing looks good; minor robustness tweak optional.

If a parse function returns undefined/null, Object.assign will no-op but reads cleaner guarding the result.

-        try {
-          Object.assign(strictParams, strictParseParams(strictParams as any))
+        try {
+          const parsed = strictParseParams(strictParams as any)
+          if (parsed) Object.assign(strictParams, parsed)
packages/react-router/tests/useParams.test.tsx (2)

182-199: Simplify act usage and avoid wrapping fireEvent with act.

react-testing-library already wraps fireEvent in act. Use a single awaited act for router.load and click directly for events.

-  await act(() => router.load())
+  await act(async () => { await router.load() })
...
-  await act(() => fireEvent.click(firstCategoryLink))
+  fireEvent.click(firstCategoryLink)
...
-  await act(() => fireEvent.click(all-category-link))
+  fireEvent.click(allCategoryLink)
...
-  await act(() => fireEvent.click(secondPostLink))
+  fireEvent.click(secondPostLink)

Also applies to: 226-236


210-215: Specify radix and guard null when parsing textContent.

Avoids subtle parse issues and TS nullability warnings.

-  let renderedPost = {
-    id: parseInt(postIdValue.textContent),
+  let renderedPost = {
+    id: parseInt(postIdValue.textContent!, 10),
     title: postTitleValue.textContent,
     category: postCategory.textContent,
   }
...
-  renderedPost = {
-    id: parseInt(postIdValue.textContent),
+  renderedPost = {
+    id: parseInt(postIdValue.textContent!, 10),
     title: postTitleValue.textContent,
     category: postCategory.textContent,
   }

Also applies to: 247-252

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6167c0c and b5d3686.

📒 Files selected for processing (3)
  • packages/react-router/tests/useParams.test.tsx (1 hunks)
  • packages/router-core/src/router.ts (4 hunks)
  • packages/solid-router/tests/useParams.test.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/router-core/src/router.ts (1)
packages/router-core/src/path.ts (1)
  • interpolatePath (371-485)
⏰ 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/solid-router/tests/useParams.test.tsx (1)

138-149: Using useParams({ from: postRoute.fullPath }) here is correct and exercises strict parsing.

This ensures leaf-route scoped parsed params are read. Nice.

packages/router-core/src/router.ts (2)

1219-1226: Verify matchId stability with route.id-based interpolation.

Switching matchId to interpolatePath(route.id, { leaveWildcards: true }) changes the identity key. Please confirm no collisions for routes with optional/wildcard segments (e.g., siblings differing only by optional params) and that cache hits remain correct across param changes.

Would you run a quick check in your suite to ensure identical URLs across reloads resolve to the same match.id and that toggling optional params yields distinct match ids where expected?

Also applies to: 1258-1259


1274-1282: Correctly plumbs _strictParams and paramsError into matches.

This aligns the match object with strict parsing semantics and per-route error surfacing.

Also applies to: 1299-1311

packages/react-router/tests/useParams.test.tsx (1)

139-170: LGTM: useParams is scoped to postRoute ensuring parsed values are asserted.

Good coverage of category mapping and numeric id parsing, consistent with stringify handlers.

@nlynzaad nlynzaad merged commit 8f8f03f into main Sep 11, 2025
6 checks passed
@nlynzaad nlynzaad deleted the parse_strictParams branch September 11, 2025 07:27
LadyBluenotes pushed a commit to LadyBluenotes/router that referenced this pull request Sep 19, 2025
with changing useParams to make use of _strictParams it became apparent
that _strictParams did not apply param parsing correctly. This PR
resolves that problem and hence useParams now return the parsed params
correctly.

This PR also adds unit tests in both react-router and solid-router to
test this expected result.

A separate PR has been created to merge into alpha (TanStack#5121)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Features
- Stricter route parameter parsing and validation with support for
custom parse/stringify.
- Clearer per-route parameter error reporting, with optional
throw-on-error behavior.
  - More consistent parameter values across nested routes and loaders.

- Tests
- Added integration tests validating useParams with nested routing,
custom param parsing/stringifying, and loader data in both React and
Solid routers.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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.

1 participant