Skip to content

fix(router-core): dehydrate all manifest routes so client-side navigations restore shared-chunk CSS - #8225

Open
breken-ai wants to merge 3 commits into
TanStack:mainfrom
breken-ai:patch-8224
Open

fix(router-core): dehydrate all manifest routes so client-side navigations restore shared-chunk CSS#8225
breken-ai wants to merge 3 commits into
TanStack:mainfrom
breken-ai:patch-8224

Conversation

@breken-ai

@breken-ai breken-ai commented Sep 3, 2026

Copy link
Copy Markdown

Summary

Fixes #8224 - on the first client-side navigation away from the SSR'd entry route, a route whose component shares a chunk with an already-loaded route loses all of its CSS, and the stylesheet is never restored.

Root cause: dehydrate() in packages/router-core/src/ssr/ssr-server.ts serialized only the SSR-matched routes into the dehydrated manifest, while the render-time router.ssr.manifest getter exposes all routes. On the client, HeadContent removes the outgoing route's stylesheet links on navigation and looks the incoming route's css up in the dehydrated manifest. With the incoming route's entry pruned, nothing re-declares the stylesheet, and Vite won't re-inject it because the chunk's JS module is already in the browser's module cache. The page renders unstyled until a hard refresh.

Fix: dehydrate() now dehydrates all routes, consistent with the render-time getter:

  • inlineCss off: manifest.routes (every route, untouched)
  • inlineCss on: { ...manifest.routes, ...preparedManifest.routes } - matched routes keep their stripped entries (their inlined styles are already in the page); unmatched routes dehydrate as-is so HeadContent can re-declare their assets on client-side navigation

Minimal reproduction: https://github.com/breken-ai/tanstack-router-8224-repro (React 18.3.1 - React 19's stylesheet hoisting masks the bug). Load /, client-navigate to /pool/123: the shared chunk's stylesheet link is removed from <head> and the page loses all styles. With this patch applied, the styles survive navigation.

Test plan

  • New regression test tests/issue-8224-dehydrated-manifest-all-routes.test.ts (2 tests) asserting the dehydrated manifest contains every route's assets. Both fail on the unfixed code and pass with the fix (negative control verified).
  • Three existing tests in tests/ssr-server-manifest.test.ts encoded the pruning as intended behavior and were updated to the new contract (renames included):
    • omits unmatched route assets by default -> dehydrates assets for all routes, not just the SSR matches
    • inlineCss stripping assertions are now scoped to matched routes; unmatched routes keep their stylesheet links
    • omits descendant assets past a terminal parent boundary -> dehydrates descendant assets past a terminal parent boundary
  • Full @tanstack/router-core suite: 108 files, 1615 tests passed, 0 failed, no type errors.
  • Verified end-to-end with a playwright repro: unpatched build drops padding 20px 20px 15px -> 0px and removes the stylesheet link on navigation; patched build keeps both.

Built by breken, your AI support engineer - breken.ai - this one's on us.

Summary by CodeRabbit

  • Bug Fixes
    • Server-rendered applications now include assets for all configured routes in the dehydrated manifest, including routes that were not part of the current request.
    • Unmatched route stylesheets and preload assets are preserved during SSR.
    • When inline CSS is enabled, matched-route styles continue to be handled inline while unmatched-route stylesheet links remain available.

@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: a13801b2-3b17-440f-a3a7-333a9631dec7

📥 Commits

Reviewing files that changed from the base of the PR and between 37877da and c67fd5d.

📒 Files selected for processing (3)
  • packages/router-core/src/ssr/ssr-server.ts
  • packages/router-core/tests/issue-8224-dehydrated-manifest-all-routes.test.ts
  • packages/router-core/tests/ssr-server-manifest.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

SSR manifest dehydration now includes assets for all routes. Matched routes still remove inlined CSS entries and emit placeholders. Unmatched routes retain their CSS and preload entries for later client-side navigation.

Changes

SSR Manifest Dehydration

Layer / File(s) Summary
Merge complete route manifest
packages/router-core/src/ssr/ssr-server.ts
Dehydration uses the full route manifest and overlays prepared matched routes with inline-CSS-stripped assets.
Validate route asset preservation
packages/router-core/tests/issue-8224-dehydrated-manifest-all-routes.test.ts, packages/router-core/tests/ssr-server-manifest.test.ts
Tests verify CSS and preload entries for unmatched routes, stripped styles for matched routes, inline-style placeholders, and descendant assets past terminal parent boundaries.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to c67fd

SSR dehydration now retains route assets needed for later client navigation while preserving inline-CSS behavior for rendered routes. The covered manifest behavior is ready to merge.

Suggested reviewers: sheraff

🚥 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%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the primary change: SSR dehydration now includes all manifest routes to preserve shared-chunk CSS during client-side navigation.
Description check ✅ Passed The description provides a detailed summary, root cause, fix, test plan, regression coverage, and validation results. It does not use the template headings or include the checklist and release-impact …
Linked Issues check ✅ Passed The implementation satisfies issue #8224 by dehydrating all manifest routes, preserving unmatched-route assets, and retaining prepared entries for matched routes when inline CSS is enabled. The added …
Out of Scope Changes check ✅ Passed The source change and all test changes directly support the linked issue and stated objective. No unrelated code or scope was identified.
Full details: Description check

Explanation

The description provides a detailed summary, root cause, fix, test plan, regression coverage, and validation results. It does not use the template headings or include the checklist and release-impact sections, but the required change context is substantially complete.

Full details: Linked Issues check

Explanation

The implementation satisfies issue #8224 by dehydrating all manifest routes, preserving unmatched-route assets, and retaining prepared entries for matched routes when inline CSS is enabled. The added and updated tests cover the required behavior.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Route CSS removed on first client-side navigation and never restored (client manifest is pruned to SSR'd matches)

1 participant