Skip to content

refactor: signal based reactivity - #6704

Merged
Sheraff merged 100 commits into
mainfrom
refactor-signals
Mar 20, 2026
Merged

refactor: signal based reactivity#6704
Sheraff merged 100 commits into
mainfrom
refactor-signals

Conversation

@Sheraff

@SheraffSheraff commented Feb 19, 2026

Copy link
Copy Markdown
Collaborator

Benchmarks

Custom benchmarks (separate from the codspeed ones), made to exaggerate the impact of this PR:

  • total_cycle_ms is a Router test, runs on playwright, measures "how long to navigate 100 times" on a rerender-heavy page
  • median_req_per_sec is a Start test, runs on autocannon, using the previously established "minimal", "100 links", "many nested routes" apps

Results:

  • Nav total_cycle_ms (median of trials), lower is better:
BenchmarkBaseHeadChange
React853.90265.403.2x
Solid823.55342.102.4x
Vue1395.15214.256.5x
  • SSR median_req_per_sec (median of rounds), higher is better:
BenchmarkBaseHeadChange
React root_fixed5022.205033.15+0.22%
React page100_fixed1170.501170.60+0.01%
React page_random1185.661186.00+0.03%
Solid root_fixed1177.841178.60-0.06%
Solid page100_fixed139.07139.04+0.02%
Solid page_random139.97139.57+0.29%
Vue root_fixed1387.641389.57+0.14%
Vue page100_fixed328.24328.64+0.12%
Vue page_random284.44284.80+0.13%

Summary by CodeRabbit

  • New Features

    • Enhanced server/client reactivity and SSR paths across React, Solid, and Vue; more granular per-route reactivity exposed to UIs and devtools.
  • Bug Fixes

    • Stabilized tests by replacing flaky range assertions with exact expected values; E2E tests adjusted for deterministic renders.
  • Documentation

    • Removed documented pendingMatches field and updated hook wording to reference pending vs active matches.
  • Refactor

    • Router state moved to store-backed match APIs; public RouterState no longer exposes pendingMatches/cachedMatches.

@coderabbitai

coderabbitaiBot commented Feb 19, 2026

Copy link
Copy Markdown
Contributor

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.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replace the central Router __store with a granular, server-aware RouterStores system; RouterCore now accepts a store factory; public RouterState drops pendingMatches/cachedMatches; adapters, devtools, plugins, and tests updated to use per-store subscriptions and adapter-specific store factories.

Changes

Cohort / File(s)Summary
Core stores & RouterCore
packages/router-core/src/stores.ts, packages/router-core/src/router.ts, packages/router-core/src/index.ts
Add comprehensive RouterStores implementation, per-match stores, store factory types and exports; RouterCore now accepts GetStoreConfig, exposes stores and batch; public RouterState no longer exposes pendingMatches/cachedMatches. Review types, constructor changes, and public-state shape.
Router-core logic & SSR
packages/router-core/src/load-matches.ts, packages/router-core/src/ssr/*, packages/router-core/src/scroll-restoration.ts, packages/router-core/src/createRequestHandler.ts
Migrate all reads/writes from router.state to router.stores.* and snapshots; add hasForcePendingActiveMatch helper; align batching to router.batch; hydration/dehydration use activeMatchesSnapshot. Check batching/scoping and SSR snapshots.
Store factories (framework adapters)
packages/react-router/src/routerStores.ts, packages/solid-router/src/routerStores.ts, packages/vue-router/src/routerStores.ts
Introduce adapter-specific getStoreFactory implementations (server: non-reactive stores, client: reactive stores) and derived stores (e.g., childMatchIdByRouteId, pendingRouteIds). Validate init hooks and derived store correctness.
React adapter
packages/react-router/src/... (Match.tsx, Matches.tsx, Scripts.tsx, Transitioner.tsx, headContentUtils.tsx, link.tsx, not-found.tsx, useMatch.tsx, useLocation.tsx, useCanGoBack.ts, useRouterState.tsx, router.ts)
Replace useRouterState with useStore/router.stores access; add server/client branches, per-match subscriptions, structural sharing, and new asset/head/script helpers. Inspect server branches, per-match store subscriptions, and error/redirect handling.
Solid adapter
packages/solid-router/src/... (Match.tsx, Matches.tsx, Scripts.tsx, Transitioner.tsx, headContentUtils.tsx, link.tsx, useMatch.tsx, useLocation.tsx, useRouterState.tsx, routerStores.ts, store.ts, matchContext.tsx)
Migrate to Solid signals/memos reading router.stores; add routeIdContext/pendingMatchContext, shallow helpers, and SSR-aware non-reactive paths via getStoreFactory. Review memo equality, context propagation, and SSR non-reactive paths.
Vue adapter
packages/vue-router/src/... (Match.tsx, Matches.tsx, Scripts.tsx, Transitioner.tsx, headContentUtils.tsx, link.tsx, useMatch.tsx, useLocation.tsx, useRouterState.tsx, routerStores.ts, matchContext.tsx)
Migrate to useStore/computed reading router.stores; add routeId/pending contexts, SSR branches and derived computed stores (lastMatchRouteFullPath, childMatchIdByRouteId, pendingRouteIds). Validate computed derivations and SSR vs client paths.
Devtools & plugin
packages/router-devtools-core/src/BaseTanStackRouterDevtoolsPanel.tsx, packages/router-plugin/src/core/route-hmr-statement.ts, packages/router-plugin/tests/add-hmr/snapshots/*
Devtools now consume pending/cached snapshots from stores; plugin/HMR use activeMatchesSnapshot/pendingMatchesSnapshot instead of router.state arrays. Check devtools rendering and HMR detection changes.
Tests & test utilities
packages/router-core/tests/*, packages/router-core/tests/routerTestUtils.ts, e2e/*
Add createTestRouter helper that supplies getStoreConfig; tests updated to use stores/snapshots and tightened exact update counts; add granular-stores tests; minor e2e tweaks (burstClicks, render count). Review test helpers and updated assertions.
Docs & types
docs/router/api/router/RouterStateType.md, docs/router/api/router/useChildMatchesHook.md, docs/router/api/router/useParentMatchesHook.md
Remove pendingMatches from RouterState docs and reword hooks to reference pending vs active matches. Ensure docs match public types.
Package changes & utils
packages/router-core/package.json, packages/solid-router/package.json, packages/router-core/src/utils.ts, packages/router-core/src/utils/batch.ts
Move @tanstack/store to devDeps in router-core; remove @tanstack/solid-store from solid-router deps; add arraysEqual util; delete legacy batch utility (batch.ts). Verify package compat and removed batch usages replaced by router.batch.
Store update report
store-updates-delta.md
Add adapter-specific delta report documenting observed update-count changes across React/Solid/Vue tests. Informational only.

Sequence Diagram(s)

sequenceDiagram
participant Client as Client App
participant StoreFactory as Store Factory (getStoreFactory)
participant ReactiveStores as RouterStores (per-adapter)
participant MatchComp as Match component
Client->>StoreFactory: initialize Router (isServer: false)
StoreFactory->>ReactiveStores: create reactive stores + batch + init
ReactiveStores-->>StoreFactory: return stores
Client->>MatchComp: render Match
MatchComp->>ReactiveStores: subscribe to per-match store (useStore/useStore-like)
ReactiveStores-->>MatchComp: push per-match updates
MatchComp-->>Client: re-render UI
Loading
sequenceDiagram
participant Server as SSR
participant StoreFactory as Store Factory (getStoreFactory)
participant SSRStores as Non-reactive RouterStores
participant MatchComp as Match component
Server->>StoreFactory: initialize Router (isServer: true)
StoreFactory->>SSRStores: create non-reactive stores (snapshots)
SSRStores-->>StoreFactory: return stores
Server->>MatchComp: render Match (read-only)
MatchComp->>SSRStores: read store.state directly
SSRStores-->>MatchComp: return snapshot
MatchComp-->>Server: produce HTML
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • nlynzaad

Poem

🐰 Hop, hop — the stores split up the load,
Per-match burrows keep each route in code.
SSR naps quiet while client gardens grow,
Batches hum softly, snapshots steal the show.
A rabbit cheers — small hops, big flow!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 25.76% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'refactor: signal based reactivity' is directly related to the main change: replacing useRouterState with store-based reactivity and introducing signal-based state management across React, Solid, and Vue routers.

✏️ 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-signals
📝 Coding Plan
  • Generate coding plan for human review comments

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

@nx-cloud

nx-cloudBot commented Feb 19, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit 06c3127

CommandStatusDurationResult
nx affected --targets=test:eslint,test:unit,tes...✅ Succeeded10m 32sView ↗
nx run-many --target=build --exclude=examples/*...✅ Succeeded2m 8sView ↗

☁️ Nx Cloud last updated this comment at 2026-03-19 19:16:08 UTC

@pkg-pr-new

pkg-pr-newBot commented Feb 19, 2026

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: 06c3127

Base automatically changed from codex/store-0.9.1-migration to mainFebruary 19, 2026 21:27
@github-actions

github-actionsBot commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

12 package(s) bumped directly, 19 bumped as dependents.

🟨 Minor bumps

PackageVersionReason
@tanstack/react-router1.167.5 → 1.168.0Changeset
@tanstack/react-start1.166.18 → 1.167.0Changeset
@tanstack/router-core1.167.5 → 1.168.0Changeset
@tanstack/router-devtools-core1.166.9 → 1.167.0Changeset
@tanstack/router-plugin1.166.14 → 1.167.0Changeset
@tanstack/router-ssr-query-core1.166.9 → 1.167.0Changeset
@tanstack/solid-router1.167.5 → 1.168.0Changeset
@tanstack/solid-start1.166.18 → 1.167.0Changeset
@tanstack/start-client-core1.166.13 → 1.167.0Changeset
@tanstack/start-server-core1.166.13 → 1.167.0Changeset
@tanstack/vue-router1.167.5 → 1.168.0Changeset
@tanstack/vue-start1.166.18 → 1.167.0Changeset

🟩 Patch bumps

PackageVersionReason
@tanstack/react-router-devtools1.166.9 → 1.166.10Dependent
@tanstack/react-router-ssr-query1.166.9 → 1.166.10Dependent
@tanstack/react-start-client1.166.14 → 1.166.15Dependent
@tanstack/react-start-server1.166.14 → 1.166.15Dependent
@tanstack/router-cli1.166.14 → 1.166.15Dependent
@tanstack/router-devtools1.166.9 → 1.166.10Dependent
@tanstack/router-generator1.166.13 → 1.166.14Dependent
@tanstack/router-vite-plugin1.166.14 → 1.166.15Dependent
@tanstack/solid-router-devtools1.166.9 → 1.166.10Dependent
@tanstack/solid-router-ssr-query1.166.9 → 1.166.10Dependent
@tanstack/solid-start-client1.166.13 → 1.166.14Dependent
@tanstack/solid-start-server1.166.13 → 1.166.14Dependent
@tanstack/start-plugin-core1.167.2 → 1.167.3Dependent
@tanstack/start-static-server-functions1.166.15 → 1.166.16Dependent
@tanstack/start-storage-context1.166.13 → 1.166.14Dependent
@tanstack/vue-router-devtools1.166.9 → 1.166.10Dependent
@tanstack/vue-router-ssr-query1.166.9 → 1.166.10Dependent
@tanstack/vue-start-client1.166.13 → 1.166.14Dependent
@tanstack/vue-start-server1.166.13 → 1.166.14Dependent

@SheraffSheraff changed the title Refactor-signalsrefactor: signal based reactivityMar 20, 2026
@Sheraff
Sheraff merged commit 0545239 into mainMar 20, 2026
18 checks passed
@Sheraff
Sheraff deleted the refactor-signals branch March 20, 2026 09:33
tannerlinsley added a commit that referenced this pull request May 4, 2026
Three drift points fixed where the rebase made them obvious:
- packages/react-native-router/vite.config.ts: @tanstack/config/vite
was renamed to @tanstack/vite-config on main; updated import + added
tsconfigPath: './tsconfig.build.json' to match the convention.
- packages/react-native-router/tsconfig.build.json: added (matches the
pattern from router-plugin/start-plugin-core).
- packages/router-generator/src/template.ts (react-native target): main
removed config.verboseFileRoutes; the RN target now uses the same
serializeRoutePath() pattern as the react/solid/vue targets.
Remaining migration work in react-native-router (10 TS errors against
main's router-core):
- Matches.tsx uses RouterState.pendingMatches and RouterState.cachedMatches
which no longer exist on main (state was refactored as part of the
signal-based core in #6704 and follow-ups). Need to redesign the
pending-matches rendering logic against the new state shape.
- useRouterState.tsx accesses router.__store directly; main moved this to
router.stores.__store.
- Transitioner.tsx's getLocationChangeInfo signature changed.
- Router constructor now requires a getStoreConfig argument.
These are real engineering tasks (not mechanical drift) and belong to a
proper feat/react-native → main migration commit, not this batch.
Sign up for freeto 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

@Sheraff@birkskyum