Skip to content

perf(router): consolidate indexed Links into immutable subscription descriptors - #8570

Draft
schiller-manuel wants to merge 9 commits into
mainfrom
codex/link-indexed-lazy-publication
Draft

schiller-manuel wants to merge 9 commits into
mainfrom
codex/link-indexed-lazy-publication

Conversation

@schiller-manuel

@schiller-manuel schiller-manuel commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Changes

A smaller alternative to the fast #8557 implementation, built from its head 8ab0b43503498dd125a616c76089c59dc5a0abca. This PR includes its changes/history and preserves attribution. #8557 and #8566 are untouched.

Runtime target: #8557, not merely main. Keep actual build-read masks, pathname candidate indexes, retained-Link filtering, departure reconciliation, native callback dependencies, and one subscription per Link.

Current revision: 2184c2e56ab4463c8163f014fb3415c771884e4b

  • Consolidate the render view and committed registration into one immutable input descriptor. Creating or reading a speculative descriptor cannot change a mounted descriptor's inputs; only subscribing registers it.
  • Let React's existing useSyncExternalStore own registration/replacement. Remove the separate store memo and layout-effect view adoption.
  • Let each Solid computation / Vue watcher own its descriptor and subscription; Vue callback effects are disposed with their descriptor.
  • Preserve eager preparation and atomic publication before notification. Destination callbacks may themselves navigate, even when subscribers only request a render and do not synchronously read.
  • Preserve previous-value acceptance tokens, transaction/configuration checks, cached final output identity, reindexing, and renderer-owned errors.
  • Keep historical location snapshots only on unsubscribed descriptors.
  • Adapt internal settlement/retention fixture setup to immutable subscriptions without weakening assertions. Public Link API and existing regression suites are unchanged.

No dependencies, lockfiles, workflows, or public Link props changed.

Iteration record and corrected comparison

The initial commit 8a4bb52 tried lazy derivation after indexed invalidation. CI rejected it: prepare-reentry exposed a callback-triggered successor navigation starting too late. Its bundle report showed no meaningful improvement at displayed precision. That experiment has been replaced, not accepted.

The current algorithm passed 25 local mock-router contract tests, including eager callback reentry, native invalidation acceptance, atomic sibling reads, resolve/reject/supersession settlement, immutable speculative inputs, equal-output notification suppression, cleanup, history formatting and a 1,000-Link retained grid. These are algorithm checks, not framework or production measurements.

Correction to the earlier description: the freshly fetched #8557 CodSpeed report shows React client-links 76.5 ms, against main 92.8 ms. The previously quoted 38.3 ms was not supported by that report and must not be used as the baseline. This PR's first (rejected) revision reported 76.4 ms. Only results attached to the current head establish its performance; cross-run measurements also need normal benchmarking caution.

Required validation

GitHub CI is the framework/build/production measurement environment for this iteration. Keep draft until the above checks establish the result. The inherited architecture/measurement reports describe #8557, not this revision. Existing published-code changesets remain included.

Sheraff and others added 6 commits September 29, 2026 03:40
* refactor: reduce centralized Link state bundle cost

* refactor: further reduce Link state bundle cost (#8563)

* refactor: further reduce Link state bundle cost

* refactor: compact Link internals with tuples (#8564)

* refactor: compact Link internals with tuples

* refactor: reduce Link registry and adapter overhead (#8565)
@coderabbitai

coderabbitai Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

6 package(s) bumped directly, 18 bumped as dependents.

🟩 Patch bumps

Package Version Reason
@tanstack/history 1.162.4 → 1.162.5 Changeset
@tanstack/react-router 1.170.40 → 1.170.41 Changeset
@tanstack/router-core 1.171.33 → 1.171.34 Changeset
@tanstack/solid-router 1.170.37 → 1.170.38 Changeset
@tanstack/start-plugin-core 1.171.48 → 1.171.49 Changeset
@tanstack/vue-router 1.170.36 → 1.170.37 Changeset
@tanstack/react-start 1.168.59 → 1.168.60 Dependent
@tanstack/react-start-client 1.168.38 → 1.168.39 Dependent
@tanstack/react-start-rsc 0.1.58 → 0.1.59 Dependent
@tanstack/react-start-server 1.167.45 → 1.167.46 Dependent
@tanstack/router-cli 1.167.39 → 1.167.40 Dependent
@tanstack/router-generator 1.167.39 → 1.167.40 Dependent
@tanstack/router-plugin 1.168.41 → 1.168.42 Dependent
@tanstack/router-vite-plugin 1.167.41 → 1.167.42 Dependent
@tanstack/solid-start 1.168.56 → 1.168.57 Dependent
@tanstack/solid-start-client 1.168.36 → 1.168.37 Dependent
@tanstack/solid-start-server 1.167.43 → 1.167.44 Dependent
@tanstack/start-client-core 1.170.33 → 1.170.34 Dependent
@tanstack/start-server-core 1.169.38 → 1.169.39 Dependent
@tanstack/start-static-server-functions 1.167.38 → 1.167.39 Dependent
@tanstack/start-storage-context 1.167.35 → 1.167.36 Dependent
@tanstack/vue-start 1.168.55 → 1.168.56 Dependent
@tanstack/vue-start-client 1.167.39 → 1.167.40 Dependent
@tanstack/vue-start-server 1.167.43 → 1.167.44 Dependent

@nx-cloud

nx-cloud Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit 2184c2e

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

☁️ Nx Cloud last updated this comment at 2026-09-30 08:53:29 UTC

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Bundle Size Benchmarks

  • Commit: 8e07e2e53bb1
  • Measured at: 2026-09-30T08:51:18.880Z
  • Baseline source: history:41ebd288677b
  • Dashboard: bundle-size history

The following scenarios have bundle-size changes compared with the baseline:

Scenario Current (gzip) Initial (gzip) Raw Brotli Trend
react-router.minimal 85.1 KiB
+1.2 KiB
85.0 KiB
+1.2 KiB
264.5 KiB
+3.1 KiB
74.3 KiB
+1.1 KiB
▁▁▁▁▁▁▁▁▁▁▁█
react-router.full 88.7 KiB
+1.2 KiB
88.6 KiB
+1.2 KiB
276.4 KiB
+3.1 KiB
77.3 KiB
+1,023 B
▁▁▁▁▁▁▁▁▁▁▁█
solid-router.minimal 34.8 KiB
+1.3 KiB
34.7 KiB
+1.3 KiB
99.3 KiB
+3.3 KiB
31.5 KiB
+1.2 KiB
▁▁▁▁▁▁▁▁▁▁▁█
solid-router.full 39.7 KiB
+1.2 KiB
39.5 KiB
+1.2 KiB
114.1 KiB
+3.3 KiB
35.7 KiB
+1.1 KiB
▁▁▁▁▁▁▁▁▁▁▁█
vue-router.minimal 51.2 KiB
+1.8 KiB
51.1 KiB
+1.8 KiB
141.5 KiB
+4.7 KiB
46.4 KiB
+1.7 KiB
▁▁▁▁▁▁▁▁▁▁▁█
vue-router.full 56.8 KiB
+1.8 KiB
56.7 KiB
+1.8 KiB
159.8 KiB
+4.7 KiB
51.4 KiB
+1.7 KiB
▁▁▁▁▁▁▁▁▁▁▁█
react-start.minimal 98.0 KiB
+1.2 KiB
97.8 KiB
+1.2 KiB
305.5 KiB
+3.1 KiB
85.0 KiB
+966 B
▁▁▁▁▃▃▃▃▃▃▃█
react-start.query-integration 105.5 KiB
+1.3 KiB
105.3 KiB
+1.3 KiB
332.1 KiB
+3.1 KiB
91.4 KiB
+941 B
▁▁▁▁▃▃▃▃▃▃▃█
react-start.deferred-hydration 98.7 KiB
+1.2 KiB
97.8 KiB
+1.2 KiB
306.8 KiB
+3.1 KiB
85.7 KiB
+1,015 B
▁▁▁▁▃▃▃▃▃▃▃█
react-start.full 101.1 KiB
+1.2 KiB
101.0 KiB
+1.2 KiB
315.2 KiB
+3.1 KiB
87.7 KiB
+1.1 KiB
▁▁▁▁▃▃▃▃▃▃▃█
react-start.rsbuild.minimal 101.5 KiB
+1.2 KiB
101.3 KiB
+1.2 KiB
316.5 KiB
+3.1 KiB
87.6 KiB
+985 B
▁▁▁▁▃▃▃▃▄▄▄█
react-start.rsbuild.minimal-iife 101.9 KiB
+1.2 KiB
101.7 KiB
+1.2 KiB
317.4 KiB
+3.1 KiB
88.0 KiB
+1.1 KiB
▁▁▁▁▃▃▃▃▄▄▄█
react-start.rsbuild.full 104.7 KiB
+1.1 KiB
104.5 KiB
+1.1 KiB
326.5 KiB
+3.1 KiB
90.4 KiB
+1.0 KiB
▁▁▁▁▃▃▃▃▄▄▄█
solid-start.minimal 47.6 KiB
+1.2 KiB
47.4 KiB
+1.2 KiB
139.1 KiB
+3.3 KiB
42.4 KiB
+1.1 KiB
▁▁▁▁▃▃▃▃▃▃▃█
solid-start.deferred-hydration 50.6 KiB
+1.2 KiB
47.5 KiB
+1.2 KiB
146.4 KiB
+3.3 KiB
45.2 KiB
+1.1 KiB
▁▁▁▁▃▃▃▃▃▃▃█
solid-start.full 52.7 KiB
+1.2 KiB
52.5 KiB
+1.2 KiB
154.4 KiB
+3.3 KiB
46.8 KiB
+1.2 KiB
▁▁▁▁▃▃▃▃▃▃▃█
vue-start.minimal 67.3 KiB
+1.8 KiB
67.1 KiB
+1.8 KiB
191.0 KiB
+4.7 KiB
60.0 KiB
+1.6 KiB
▁▁▁▁▃▃▃▃▃▃▃█
vue-start.full 71.1 KiB
+1.8 KiB
71.0 KiB
+1.8 KiB
203.3 KiB
+4.7 KiB
63.3 KiB
+1.6 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

pkg-pr-new Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-rsc

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: 82f3fe3

@codspeed

codspeed Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Merging this PR will regress 14 benchmarks

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

⚡ 20 improved benchmarks
❌ 14 regressed benchmarks
✅ 157 untouched benchmarks
⏩ 3 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ Simulation client-links navigation loop (solid) 451.9 ms 685.4 ms -34.07%
❌ Simulation client-route-tree-scale navigation loop (solid) 262.3 ms 388.8 ms -32.54%
❌ Simulation client-async-pipeline navigation loop (solid) 126.7 ms 176.3 ms -28.16%
❌ Simulation client-rewrites navigation loop (solid) 152 ms 207.6 ms -26.78%
❌ Simulation hydrate restored route state and mixed Links (solid) 400 ms 533.9 ms -25.07%
❌ Simulation client-side navigation loop (solid) 192.6 ms 243.1 ms -20.76%
❌ Simulation client-control-flow navigation loop (solid) 119.1 ms 149.9 ms -20.56%
❌ Simulation client-head navigation loop (solid) 419.3 ms 486.1 ms -13.74%
❌ Simulation client-history navigation loop (solid) 105.6 ms 120.2 ms -12.11%
❌ Simulation client-search-params navigation loop (solid) 251.2 ms 285.5 ms -12.02%
❌ Simulation client-loaders navigation loop (solid) 157.5 ms 177.1 ms -11.08%
❌ Simulation client-preload interaction loop (solid) 159.5 ms 176 ms -9.36%
❌ Simulation client-nested-params navigation loop (solid) 777.2 ms 805.3 ms -3.49%
❌ Memory mem server error-paths unmatched (react) 858.8 KB 889.8 KB -3.48%
⚡ Simulation client-links navigation loop (vue) 294.1 ms 71.6 ms ×4.1
⚡ Memory mem server streaming-peak chunked (react) 642.3 KB 350 KB +83.54%
⚡ Simulation ssr request loop (solid) 256.8 ms 206.1 ms +24.57%
⚡ Simulation client-route-tree-scale navigation loop (vue) 143.1 ms 115.4 ms +24.07%
⚡ Simulation client-links navigation loop (react) 92.8 ms 75.9 ms +22.22%
⚡ Simulation client-control-flow navigation loop (vue) 74.7 ms 63.8 ms +17.19%
... ... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing codex/link-indexed-lazy-publication (82f3fe3) with main (41ebd28)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

…escriptors

Restore eager preparation and atomic publication before observer notification.
Unify the input descriptor and subscription lifetime across React, Solid and Vue,
removing React's extra memo/layout-effect adoption and core view indirection.
Preserve dependency masks, pathname indexing, error ownership and settlement guards.
Adapt internal fixture setup without weakening the settlement assertions.
@schiller-manuel schiller-manuel changed the title perf(router): keep indexed Link updates with lazy snapshot publication perf(router): consolidate indexed Links into immutable subscription descriptors Sep 30, 2026

This branch has not been deployed

No deployments
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.

2 participants