Skip to content

perf(router-core): reduce sync match update churn#7019

Open
Sheraff wants to merge 2 commits intomainfrom
flo/load-matches-sync-fastpaths
Open

perf(router-core): reduce sync match update churn#7019
Sheraff wants to merge 2 commits intomainfrom
flo/load-matches-sync-fastpaths

Conversation

@Sheraff
Copy link
Contributor

@Sheraff Sheraff commented Mar 23, 2026

Summary

  • avoid publishing synthetic beforeLoad fetch-state cycles for routes that do not actually run beforeLoad
  • collapse synchronous beforeLoad and synchronous loaderData updates so loadMatches does less per-match store churn during navigation
  • keep the change scoped to load-matches, with a small router bundle delta and a strong local React client-nav improvement

Benchmark Notes

  • Local @benchmarks/client-nav: React mean 29.6119ms -> 25.0985ms (~15.2% faster)
  • Local @benchmarks/client-nav: Solid/Vue were roughly flat within local noise (24.7548ms -> 25.1032ms, 22.5473ms -> 22.7921ms)
  • Local @benchmarks/bundle-size: router scenario gzip deltas ranged from +68B to +97B

Testing

  • CI=1 NX_DAEMON=false pnpm nx run @benchmarks/client-nav:test:perf --outputStyle=stream --skipRemoteCache
  • CI=1 NX_DAEMON=false pnpm nx run @benchmarks/bundle-size:build --outputStyle=stream --skipRemoteCache
  • CI=1 NX_DAEMON=false pnpm nx run @tanstack/react-router:test:unit --outputStyle=stream --skipRemoteCache -- tests/store-updates-during-navigation.test.tsx tests/routeContext.test.tsx

Summary by CodeRabbit

  • Refactor
    • Improved router pre-load and loader-data handling to avoid unnecessary fetch cycles and redundant fetching-state toggles, and to better handle synchronous completion paths.
  • Tests
    • Added a test covering cancelation behavior when component preloading overlaps with pre-load resolution; verifies abort is triggered exactly once and navigation completes.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 23, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8d1715dd-7cf7-419a-8e69-25286a57de90

📥 Commits

Reviewing files that changed from the base of the PR and between 1fa6a10 and 4fc676c.

📒 Files selected for processing (2)
  • packages/router-core/src/load-matches.ts
  • packages/router-core/tests/load.test.ts

📝 Walkthrough

Walkthrough

Refactors load handling: executeBeforeLoad gains an optional fetching-state toggle and a new finishSyncBeforeLoad to complete synchronous beforeLoad paths without toggling fetch state. runLoader now sometimes defers applying loaderData and conditionally updates matches based on whether data is immediately available.

Changes

Cohort / File(s) Summary
Load matching core
packages/router-core/src/load-matches.ts
Added finishSyncBeforeLoad and an optional updateFetchingState flag in executeBeforeLoad; changed control flow for routes with no beforeLoad; refined updateContext branching; altered runLoader to introduce nextLoaderData/hasDeferredLoaderData and conditionally apply loaderData.
Tests: cancel/preload scenario
packages/router-core/tests/load.test.ts
Added a test verifying router.cancelMatches() behavior when beforeLoad resolves synchronously enough to start component preload while component preload remains pending; asserts abort called once and navigation completes after resolving preload.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant Router as Router
  participant Exec as executeBeforeLoad
  participant Loader as runLoader
  participant Comp as Component.preload
  participant Abort as AbortController

  Client->>Router: navigate(to: '/foo')
  Router->>Exec: executeBeforeLoad(match)
  Exec->>Abort: create AbortController
  Exec->>Router: finishSyncBeforeLoad (sets __beforeLoadContext)
  Exec->>Loader: runLoader(match, abortSignal)
  Loader->>Comp: start component.preload()
  Note right of Comp: preload pending (blocks)
  Client->>Router: cancelMatches()
  Router->>Abort: call abort()
  Abort-->>Exec: abort event -> beforeLoad/loader receive abort
  Comp->>Loader: preload resolves (after cancel)
  Loader-->>Router: finalize navigation promise
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through code at dawn,
beforeLoad finished, then I yawn,
fetch flags flipped with gentle tap,
loaders paused mid-preload nap,
one abort — then on we prance! 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'perf(router-core): reduce sync match update churn' directly and clearly describes the main performance optimization objective of the changeset—reducing synchronous store update operations during navigation in router-core.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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 flo/load-matches-sync-fastpaths

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

@nx-cloud
Copy link

nx-cloud bot commented Mar 23, 2026

View your CI Pipeline Execution ↗ for commit 4fc676c

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

☁️ Nx Cloud last updated this comment at 2026-03-23 18:16:22 UTC

@github-actions
Copy link
Contributor

github-actions bot commented Mar 23, 2026

🚀 Changeset Version Preview

No changeset entries found. Merging this PR will not cause a version bump for any packages.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 23, 2026

Bundle Size Benchmarks

  • Commit: 8d5ea2d24efe
  • Measured at: 2026-03-23T18:00:14.245Z
  • Baseline source: history:616481641456
  • Dashboard: bundle-size history
Scenario Current (gzip) Delta vs baseline Raw Brotli Trend
react-router.minimal 88.25 KiB +101 B (+0.11%) 278.58 KiB 76.69 KiB ▁██████▅▅▅▅▆
react-router.full 91.45 KiB +72 B (+0.08%) 289.58 KiB 79.42 KiB ▁██████▅▅▆▆▆
solid-router.minimal 35.90 KiB +103 B (+0.28%) 108.48 KiB 32.17 KiB █▃▃▃▃▃▃▁▁▁▁▂
solid-router.full 40.31 KiB +98 B (+0.24%) 121.88 KiB 36.08 KiB █▃▃▃▃▃▃▁▁▁▁▂
vue-router.minimal 53.87 KiB +100 B (+0.18%) 154.71 KiB 48.29 KiB ▁██████▆▆▆▆▇
vue-router.full 58.73 KiB +91 B (+0.15%) 170.19 KiB 52.50 KiB ▁██████▆▆▇▇▇
react-start.minimal 102.62 KiB +70 B (+0.07%) 326.60 KiB 88.70 KiB ▁██████▄▄▅▅▅
react-start.full 106.03 KiB +83 B (+0.08%) 336.92 KiB 91.54 KiB ▁██████▅▅▅▅▆
solid-start.minimal 49.95 KiB +89 B (+0.17%) 154.67 KiB 44.01 KiB █▃▃▃▃▃▃▁▁▁▁▁
solid-start.full 55.44 KiB +92 B (+0.16%) 170.76 KiB 48.67 KiB █▄▄▄▄▄▄▁▁▂▂▂

Trend sparkline is historical gzip bytes ending with this PR measurement; lower is better.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 23, 2026

More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: 4fc676c

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1fa6a1046f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@codspeed-hq
Copy link

codspeed-hq bot commented Mar 23, 2026

Merging this PR will improve performance by 3.77%

⚠️ 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

⚡ 1 improved benchmark
✅ 5 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
client-side navigation loop (react) 61.4 ms 59.1 ms +3.77%

Comparing flo/load-matches-sync-fastpaths (4fc676c) with main (6077120)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (8d5ea2d) during the generation of this report, so 6077120 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

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