Skip to content

refactor(react-router): Reduce bundle size by sharing hook structuralSharing logic#7577

Merged
Sheraff merged 2 commits into
mainfrom
refactor-react-router-hook-selector-bundle-size
Jun 7, 2026
Merged

refactor(react-router): Reduce bundle size by sharing hook structuralSharing logic#7577
Sheraff merged 2 commits into
mainfrom
refactor-react-router-hook-selector-bundle-size

Conversation

@Sheraff
Copy link
Copy Markdown
Collaborator

@Sheraff Sheraff commented Jun 7, 2026

Summary by CodeRabbit

  • Refactor
    • Optimized bundle size of @tanstack/react-router by consolidating structural sharing logic across routing hooks, eliminating code duplication and improving package efficiency while maintaining full backward compatibility and identical user-facing functionality.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 7, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR consolidates duplicated structural-sharing logic across four React Router hooks (useMatch, useMatches, useLocation, useRouterState) into a single reusable useStructuralSharing hook, reducing code duplication and bundle size. Each consumer hook now delegates selection and structural-sharing behavior to this centralized helper.

Changes

Structural Sharing Refactoring

Layer / File(s) Summary
Extract useStructuralSharing hook and refactor useMatch
packages/react-router/src/useMatch.tsx
Introduces exported useStructuralSharing hook that memoizes previous selected values and conditionally applies replaceEqualDeep for structural sharing. useMatch is refactored to use this hook, simplifying match store resolution and delegating selection/structural-sharing logic instead of inlining it.
Refactor useMatches, useLocation, and useRouterState
packages/react-router/src/Matches.tsx, packages/react-router/src/useLocation.tsx, packages/react-router/src/useRouterState.tsx
Each hook imports useStructuralSharing from ./useMatch and passes the result to useStore, replacing prior inline useRef + replaceEqualDeep patterns. Removes structural-sharing logic duplication across three hooks.
Release notes for structural sharing consolidation
.changeset/silent-ghosts-sit.md
Changeset documents @tanstack/react-router patch release with bundle-size reduction claim from consolidating structural-sharing selector logic.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • TanStack/router#7575: Directly related modification of useMatches client-side structural-sharing logic around useRef/previousResult handling for SSR compatibility.
  • TanStack/router#7503: E2E test app updates that depend on the refactored useRouterState structural-sharing and selection logic.

Suggested Labels

package: react-router

Suggested Reviewers

  • schiller-manuel

Poem

🐰 A helper hook to share the load,
Four hooks walk a cleaner road,
Refs and deep-checks unified,
Bundle slim, complexity tied!
Rabbit cheers this tidy tide. 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.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 refactoring: extracting shared useStructuralSharing logic from multiple hooks to reduce bundle size.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor-react-router-hook-selector-bundle-size

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

@nx-cloud
Copy link
Copy Markdown
Contributor

nx-cloud Bot commented Jun 7, 2026

View your CI Pipeline Execution ↗ for commit 03d3141

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 3m 5s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-06-07 17:18:46 UTC

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 7, 2026

🚀 Changeset Version Preview

1 package(s) bumped directly, 4 bumped as dependents.

🟩 Patch bumps

Package Version Reason
@tanstack/react-router 1.170.15 → 1.170.16 Changeset
@tanstack/react-start 1.168.25 → 1.168.26 Dependent
@tanstack/react-start-client 1.168.13 → 1.168.14 Dependent
@tanstack/react-start-rsc 0.1.24 → 0.1.25 Dependent
@tanstack/react-start-server 1.167.19 → 1.167.20 Dependent

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 7, 2026

Bundle Size Benchmarks

  • Commit: 4423825306c6
  • Measured at: 2026-06-07T16:50:11.589Z
  • Baseline source: history:b80781ede404
  • Dashboard: bundle-size history
Scenario Current (gzip) Delta vs baseline Initial gzip Raw Brotli Trend
react-router.minimal 87.25 KiB -2 B (-0.00%) 87.11 KiB 273.66 KiB 75.83 KiB ▁▁███▃▃▃▃▃▃▃
react-router.full 90.98 KiB -103 B (-0.11%) 90.84 KiB 285.55 KiB 79.04 KiB ▄▄███▇▇▇▇▇▇▁
solid-router.minimal 35.49 KiB 0 B (0.00%) 35.36 KiB 105.96 KiB 32.00 KiB ▁▁▆▆▆██████
solid-router.full 40.54 KiB 0 B (0.00%) 40.41 KiB 121.17 KiB 36.49 KiB ▁▁███▆▆▆▆▆▆
vue-router.minimal 52.97 KiB 0 B (0.00%) 52.84 KiB 149.97 KiB 47.65 KiB ▁▁█████████
vue-router.full 58.95 KiB 0 B (0.00%) 58.82 KiB 168.73 KiB 52.81 KiB ▁▁███▇▇▇▇▇▇
react-start.minimal 101.88 KiB 0 B (0.00%) 101.74 KiB 321.97 KiB 88.08 KiB ▁▁███▇▇▇▇▇▇
react-start.deferred-hydration 102.62 KiB +8 B (+0.01%) 101.76 KiB 323.35 KiB 88.80 KiB ▁▁███▅▅▅▅▅▅█
react-start.full 105.27 KiB -60 B (-0.06%) 105.13 KiB 331.91 KiB 91.12 KiB ▁▁▅▅▅▅▅████▃
react-start.rsbuild.minimal 99.58 KiB -17 B (-0.02%) 99.41 KiB 316.42 KiB 85.71 KiB ▁▁▆▆▆██████▄
react-start.rsbuild.minimal-iife 99.98 KiB -14 B (-0.01%) 99.82 KiB 317.35 KiB 86.11 KiB ▁▁▆▆▆██████▅
react-start.rsbuild.full 102.81 KiB -84 B (-0.08%) 102.64 KiB 326.47 KiB 88.41 KiB ▃▃▄▄▄▅▅████▁
solid-start.minimal 49.60 KiB 0 B (0.00%) 49.47 KiB 152.02 KiB 43.81 KiB ▁▁▆▆▆██████
solid-start.deferred-hydration 52.86 KiB 0 B (0.00%) 49.53 KiB 160.06 KiB 46.77 KiB ▁▁▆▆▆██████
solid-start.full 55.41 KiB 0 B (0.00%) 55.28 KiB 169.07 KiB 48.79 KiB ▁▁▄▄▄▂▂████
vue-start.minimal 71.01 KiB 0 B (0.00%) 70.89 KiB 207.11 KiB 62.92 KiB ▁▁█████████
vue-start.full 75.01 KiB 0 B (0.00%) 74.88 KiB 219.75 KiB 66.39 KiB ▁▁▄▄▄▃▃████

Current gzip tracks all emitted client JS chunks. Initial gzip tracks only the entry/import graph. Trend sparkline is historical current gzip ending with this PR measurement; lower is better.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 7, 2026

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/@tanstack/arktype-adapter@7577

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7577

@tanstack/history

npm i https://pkg.pr.new/@tanstack/history@7577

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

npm i https://pkg.pr.new/@tanstack/react-start@7577

@tanstack/react-start-client

npm i https://pkg.pr.new/@tanstack/react-start-client@7577

@tanstack/react-start-rsc

npm i https://pkg.pr.new/@tanstack/react-start-rsc@7577

@tanstack/react-start-server

npm i https://pkg.pr.new/@tanstack/react-start-server@7577

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

npm i https://pkg.pr.new/@tanstack/solid-start@7577

@tanstack/solid-start-client

npm i https://pkg.pr.new/@tanstack/solid-start-client@7577

@tanstack/solid-start-server

npm i https://pkg.pr.new/@tanstack/solid-start-server@7577

@tanstack/start-client-core

npm i https://pkg.pr.new/@tanstack/start-client-core@7577

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/@tanstack/start-fn-stubs@7577

@tanstack/start-plugin-core

npm i https://pkg.pr.new/@tanstack/start-plugin-core@7577

@tanstack/start-server-core

npm i https://pkg.pr.new/@tanstack/start-server-core@7577

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

npm i https://pkg.pr.new/@tanstack/start-storage-context@7577

@tanstack/valibot-adapter

npm i https://pkg.pr.new/@tanstack/valibot-adapter@7577

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/@tanstack/virtual-file-routes@7577

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

npm i https://pkg.pr.new/@tanstack/vue-start@7577

@tanstack/vue-start-client

npm i https://pkg.pr.new/@tanstack/vue-start-client@7577

@tanstack/vue-start-server

npm i https://pkg.pr.new/@tanstack/vue-start-server@7577

@tanstack/zod-adapter

npm i https://pkg.pr.new/@tanstack/zod-adapter@7577

commit: 03d3141

Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
packages/react-router/src/useMatch.tsx (1)

30-67: Make useStructuralSharing truly internal

useStructuralSharing is exported from packages/react-router/src/useMatch.tsx, but it’s not re-exported from the public entrypoint (packages/react-router/src/index.tsx), and packages/react-router/package.json only exposes . and ./ssr/*. If it’s only for intra-package reuse, consider making it non-exported (or moving it to a private helper module) to avoid implying a public API surface.

🤖 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/react-router/src/useMatch.tsx` around lines 30 - 67, The function
useStructuralSharing is currently exported from useMatch.tsx but is intended as
an internal helper; remove the export (make it a module-private function) or
move it into a private helper file and update internal imports so it is not part
of the package public surface (adjust any internal references to
useStructuralSharing accordingly and ensure the symbol is no longer exported
from the package entrypoint).
🤖 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.

Nitpick comments:
In `@packages/react-router/src/useMatch.tsx`:
- Around line 30-67: The function useStructuralSharing is currently exported
from useMatch.tsx but is intended as an internal helper; remove the export (make
it a module-private function) or move it into a private helper file and update
internal imports so it is not part of the package public surface (adjust any
internal references to useStructuralSharing accordingly and ensure the symbol is
no longer exported from the package entrypoint).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f0782a0b-f3d0-498c-ae83-a3d7c64459d3

📥 Commits

Reviewing files that changed from the base of the PR and between 4a93cff and a1d2860.

📒 Files selected for processing (5)
  • .changeset/silent-ghosts-sit.md
  • packages/react-router/src/Matches.tsx
  • packages/react-router/src/useLocation.tsx
  • packages/react-router/src/useMatch.tsx
  • packages/react-router/src/useRouterState.tsx

Copy link
Copy Markdown
Contributor

@nx-cloud nx-cloud Bot left a comment

Choose a reason for hiding this comment

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

Important

At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.

Nx Cloud has identified a possible root cause for your failed CI:

We determined this failure is unrelated to the PR changes, which only refactor structural sharing logic within @tanstack/react-router hooks. The error originates in a compiled artifact of e2e-utils (dist/esm/users.js missing a named export), a package untouched by this PR. Our classification is environment_state — the e2e-utils dist files need to be rebuilt in the environment.

No code changes were suggested for this issue.

Trigger a rerun:

Rerun CI

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Jun 7, 2026

Merging this PR will improve performance by 82.65%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 2 improved benchmarks
❌ 1 (👁 1) regressed benchmark
✅ 3 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
ssr request loop (react) 327.9 ms 83.8 ms ×3.9
ssr request loop (vue) 481.7 ms 289.6 ms +66.36%
👁 client-side navigation loop (react) 55.8 ms 59.7 ms -6.39%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing refactor-react-router-hook-selector-bundle-size (03d3141) with main (4a93cff)

Open in CodSpeed

@Sheraff Sheraff merged commit 689d88e into main Jun 7, 2026
22 of 24 checks passed
@Sheraff Sheraff deleted the refactor-react-router-hook-selector-bundle-size branch June 7, 2026 17:29
@github-actions github-actions Bot mentioned this pull request Jun 7, 2026
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