Skip to content

fix(router): ensure useParams returns parsed params when strict is false#6387

Merged
nlynzaad merged 7 commits intoTanStack:mainfrom
jong-kyung:fix/router-params
Feb 9, 2026
Merged

fix(router): ensure useParams returns parsed params when strict is false#6387
nlynzaad merged 7 commits intoTanStack:mainfrom
jong-kyung:fix/router-params

Conversation

@jong-kyung
Copy link
Contributor

@jong-kyung jong-kyung commented Jan 15, 2026

fixes #6385

This PR fixes issue #6385, where useParams({ strict: false }) in a parent route would return unparsed parameter values from a child route.

The Fix

The fix the issue by ensuring that parsed parameters from child routes are correctly propagated to their parents when strict: false is used.

  1. Correct Parameter Propagation: The router's internal matching logic was updated. When a route match is being processed, it now correctly looks up the previous match's state, which includes its parsed parameters, and merges them. This ensures the parsed values are available throughout the route hierarchy.

  2. Performance Optimization: As part of the fix, the mechanism for retrieving previous route matches was optimized. The implementation was changed from using Array.prototype.find() to a Map-based lookup. This improves performance from O(n) to O(1), which is especially beneficial in applications with many routes.

Changes

  • packages/router-core/src/router.ts: Updated the core routing logic to correctly handle parameter propagation and implemented the Map-based optimization for match retrieval.
  • packages/react-router/tests/useParams.test.tsx: Added a new unit test to specifically verify that useParams({ strict: false }) receives parsed values.
  • e2e/react-router/basic-file-based/**: Added new routes and an e2e test to confirm the fix in a real-world file-based routing scenario.

Summary by CodeRabbit

  • New Features

    • Added a "Parsed params with strict false" page and two routes: /params-ps/strict-false and /params-ps/strict-false/$version; parent reads a numeric version param non-strictly and offers links to versioned child pages.
  • Tests

    • Added end-to-end and unit tests verifying non-strict param parsing and numeric values across parent→child navigation.
  • Chores

    • Routing performance optimization for faster internal lookups.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.
📝 Walkthrough

Walkthrough

Adds file-based routes for /params-ps/strict-false and /params-ps/strict-false/$version (with parse/stringify), UI links, E2E and unit tests validating useParams({ strict: false }) receives parsed child params after navigation, updates generated route tree, and introduces a router previous-match lookup optimization.

Changes

Cohort / File(s) Summary
Generated Route Tree
e2e/react-router/basic-file-based/src/routeTree.gen.ts
Registers /params-ps/strict-false and /params-ps/strict-false/$version: adds imports, route declarations, child wiring, FileRoutes mappings, and TanStack module augmentation entries.
New File Routes
e2e/react-router/basic-file-based/src/routes/params-ps/strict-false/route.tsx, e2e/react-router/basic-file-based/src/routes/params-ps/strict-false/$version.route.tsx
Adds parent route using useParams({ strict: false }) and child /$version route that parses/stringifies version as number; exports Route constants and components.
E2E Route UI
e2e/react-router/basic-file-based/src/routes/params-ps/index.tsx
Adds UI block with Links to the new strict-false child route instances (version 1 and 2).
Tests
e2e/react-router/basic-file-based/tests/params.spec.ts, packages/react-router/tests/useParams.test.tsx
Adds E2E and unit tests asserting parsed child params propagate to parent when using useParams({ strict: false }) after navigation.
Core Router
packages/router-core/src/router.ts
Introduces previousMatchesByRouteId Map to optimize previous-match lookups and changes match construction to prefer previousMatch?.params ?? routeParams to propagate parsed params.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as Browser/Client
  participant Router as Router Core
  participant Parent as Parent Route
  participant Child as Child Route ($version)

  Browser->>Router: Navigate to /params-ps/strict-false/1
  Router->>Child: match child route, parse params (version -> number)
  Router->>Parent: update parent match with parsed child params
  Parent->>Browser: render parent showing parsed version type/value
  Browser->>Router: Click link -> navigate to /params-ps/strict-false/2
  Router->>Child: match child route, parse params (version -> number)
  Router->>Parent: propagate updated parsed params to parent
  Parent->>Browser: re-render with new parsed version value
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • schiller-manuel

Poem

"I hopped through routes and parsed the strain,
strict:false made numbers rain.
A child sent digits up the tree,
the parent smiled—now they're numbers, whee!
nibble, click, and hop—hooray, more fun 🥕"

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: ensuring useParams returns parsed params when strict is false, which is the core objective of this PR.
Linked Issues check ✅ Passed All coding requirements from issue #6385 are met: parsed child parameters are propagated to parent routes, useParams({ strict: false }) returns consistent runtime values, and performance is improved with Map-based lookup.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the linked issue: core router logic fixes, unit tests, and end-to-end tests validating the fix with no unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

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

@nx-cloud
Copy link

nx-cloud bot commented Jan 15, 2026

View your CI Pipeline Execution ↗ for commit fffc257

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

☁️ Nx Cloud last updated this comment at 2026-02-09 00:44:05 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 15, 2026

More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-fn-stubs@6387

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router@6387

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start@6387

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: fffc257

@jong-kyung jong-kyung closed this Jan 15, 2026
@jong-kyung jong-kyung reopened this Jan 15, 2026
@jong-kyung
Copy link
Contributor Author

jong-kyung commented Jan 15, 2026

oops, clicked the wrong button and accidentally closed this! 😅 Reopening now.

Comment on lines +1537 to +1541
// Update the match's params
const previousMatch = previousMatchesByRouteId.get(match.routeId)
match.params = previousMatch
? replaceEqualDeep(previousMatch.params, routeParams)
: routeParams
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we shouldn't set the match.params in the 1st iteration then? Because it looks like we're now doing the work twice now.

Copy link
Contributor Author

@jong-kyung jong-kyung Jan 18, 2026

Choose a reason for hiding this comment

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

@Sheraff I tried removing the second update, but the regression (#6385) reappeared.

Here is why the second update is necessary:
The router processes matches from Parent to Child. When the Parent match is created in the first loop, the Child hasn't been processed yet, so routeParams doesn't strictly contain the Child's parsed params at that moment.

Since strict: false allows Parents to access Child params, we must update the Parent's match.params after the entire loop finishes (when routeParams is fully populated).

I've optimized it by piggybacking on the existing second loop (used for Context) instead of creating a new one, so the performance impact is minimal.

Please let me know if I misunderstood anything. Thank you!

@jong-kyung jong-kyung force-pushed the fix/router-params branch 4 times, most recently from 695c44a to e5ce02e Compare January 18, 2026 11:59
@jong-kyung
Copy link
Contributor Author

@nlynzaad
can you please review this?

@nlynzaad nlynzaad requested a review from Sheraff February 9, 2026 00:37
@nlynzaad nlynzaad merged commit 5c923e4 into TanStack:main Feb 9, 2026
6 checks passed
@jong-kyung
Copy link
Contributor Author

@nlynzaad Thank you for the review and merge !

Sheraff pushed a commit that referenced this pull request Feb 15, 2026
…lse (#6387)

* fix: optimize match retrieval by using a Map for previous matches

* feat: add strict false parameter handling and related routes

* replace params only when routeParams is fully parsed

---------

Co-authored-by: Nico Lynzaad <nlynzaad@zylangroup.com>
Co-authored-by: Nico Lynzaad <44094871+nlynzaad@users.noreply.github.com>
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.

useParams({ strict: false }) returns unparsed params

3 participants