Skip to content

fix(rsc): remove outdated stale hmr css references#7219

Open
jantimon wants to merge 8 commits intoTanStack:mainfrom
jantimon:fix-rsc
Open

fix(rsc): remove outdated stale hmr css references#7219
jantimon wants to merge 8 commits intoTanStack:mainfrom
jantimon:fix-rsc

Conversation

@jantimon
Copy link
Copy Markdown

@jantimon jantimon commented Apr 17, 2026

follow-up to vitejs/vite-plugin-react#1188 on the TanStack Router side where @schiller-manuel was already mentioned

While debugging CSS HMR in TanStack Start RSC (https://tanstack.com/blog/react-server-components) I noticed a second, independent problem: <link rel="stylesheet"> tags pile up in <head> across HMR edits.. Every save appends another <link> and none of them get cleaned up

CompositeComponent and RscNodeRenderer both walk the cssHrefs set reported by the RSC Flight stream and call:

ReactDOM.preinit(href, { as: 'style', precedence: 'high' })

Obviously this is a dev only problem. Vite appends an HMR cache-bust (?t=<lastHMRTimestamp>) to every CSS href on every edit (see vite-plugin-react #1188. Each edit therefore calls preinit with a different href which adds a new <link> to <head> instead of replacing the previous one

A simple possible fix would be to skip these HMR css requests by excluding css files with /[?&]t=\d+/ as Vite only emits that query in dev so:

  • Prod path is unchanged
  • Dev path cache-busted hrefs are ignored

One open question: can we somehow add HMR e2e tests to verify:

  1. changing a css prop updates with hmr
  2. removing a css prop reverts the css value to the default

Summary by CodeRabbit

  • Refactor

    • Streamlined stylesheet pre-initialization: production preinit retained; dev skips preinit to avoid stale CSS during HMR.
  • New Features

    • Added example app with routes, navigation, server-rendered demo cards (global CSS and CSS Modules), dev tooling, and Vite/RSC config.
    • Added ESLint config and .gitignore for the example.
  • Tests

    • Added Playwright e2e tests for CSS HMR, hydration helper, and test setup/teardown.
  • Chores

    • Bumped dev tool version and added a changeset entry.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 17, 2026

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

Extracted CSS preinit logic into a new helper and added a complete end-to-end RSC CSS HMR test workspace (router, routes, test components, server fns, Playwright config, Vite/TS configs, and tests).

Changes

Cohort / File(s) Summary
CSS Preinit Helper
packages/react-start-rsc/src/preinitCssHrefs.ts, packages/react-start-rsc/src/CompositeComponent.tsx, packages/react-start-rsc/src/RscNodeRenderer.tsx
Added exported preinitCssHrefs(cssHrefs) and replaced inline CSS preinit loops in CompositeComponent and RscNodeRenderer to delegate to this helper; preinit now no-ops outside production.
E2E Package Config
e2e/react-start/rsc-hmr/package.json, e2e/react-start/rsc-hmr/vite.config.ts, e2e/react-start/rsc-hmr/tsconfig.json, e2e/react-start/rsc-hmr/eslint.config.js, e2e/react-start/rsc-hmr/.gitignore
New e2e workspace files: package manifest, Vite + RSC plugins enabled, TS config, ESLint config, and .gitignore for the HMR test package.
Playwright Config & Test Lifecycle
e2e/react-start/rsc-hmr/playwright.config.ts, e2e/react-start/rsc-hmr/tests/setup/global.setup.ts, e2e/react-start/rsc-hmr/tests/setup/global.teardown.ts
Added Playwright defineConfig with dynamic port/baseURL and webServer setup plus global test setup/teardown modules to start/stop the dummy server and pre-optimize dev server.
Router & Routes
e2e/react-start/rsc-hmr/src/routeTree.gen.ts, e2e/react-start/rsc-hmr/src/router.tsx, e2e/react-start/rsc-hmr/src/routes/__root.tsx, e2e/react-start/rsc-hmr/src/routes/index.tsx, e2e/react-start/rsc-hmr/src/routes/rsc-hmr-css-modules.tsx, e2e/react-start/rsc-hmr/src/routes/rsc-hmr-global-css.tsx
Added generated route tree, router factory, root HTML route, index route, and two test routes that load server-rendered components for CSS HMR scenarios.
Test Components & Server Functions
e2e/react-start/rsc-hmr/src/utils/CssModulesCard.tsx, e2e/react-start/rsc-hmr/src/utils/CssModulesCard.module.css, e2e/react-start/rsc-hmr/src/utils/GlobalCssCard.tsx, e2e/react-start/rsc-hmr/src/utils/GlobalCssCard.css, e2e/react-start/rsc-hmr/src/utils/cssModulesCardServerComponent.tsx, e2e/react-start/rsc-hmr/src/utils/globalCssCardServerComponent.tsx
Added UI components (CSS Modules + global CSS), corresponding styles, and server functions that render those components for the routes.
Playwright Tests & Helpers
e2e/react-start/rsc-hmr/tests/rsc-hmr-css.spec.ts, e2e/react-start/rsc-hmr/tests/hydration.ts
Added end-to-end test spec that snapshots and edits on-disk CSS files to validate HMR updates and a helper to wait for client hydration.
Changeset & Root Update
.changeset/fix-rsc-stale-css-hmr.md, package.json
Added a changeset noting preinit skip in non-production; bumped root devDependency vite to ^8.0.10.

Sequence Diagram(s)

sequenceDiagram
  participant Tester as Test Runner
  participant DevServer as Dev Server (Vite)
  participant FS as File System
  participant Browser as Browser
  participant RSC as RSC Renderer
  Tester->>DevServer: start (pnpm dev:e2e)
  DevServer->>Browser: serve page (baseURL)
  Browser->>RSC: request SSR (initial render)
  RSC-->>Browser: HTML + preinit hints (preinitCssHrefs)
  Tester->>FS: edit CSS file
  FS-->>DevServer: file change event
  DevServer->>Browser: HMR update / module reload
  Browser->>RSC: apply new styles / re-render (hydration)
  Browser-->>Tester: updated computed styles observed
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through helpers, routes, and test delight,
Preinit waits in production's light,
CSS files change and HMR takes flight,
Tests nibble, watch styles swap by sight,
A rabbit cheers: styles fixed just right! 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% 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 PR title 'fix(rsc): remove outdated stale hmr css references' directly and accurately summarizes the main change—refactoring CSS preinitialization to skip cache-busted HMR hrefs and prevent stale CSS accumulation in development.
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 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 and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 17, 2026

Bundle Size Benchmarks

  • Commit: b0024d6310f1
  • Measured at: 2026-04-28T08:41:55.837Z
  • Baseline source: history:b0024d6310f1
  • Dashboard: bundle-size history
Scenario Current (gzip) Delta vs baseline Raw Brotli Trend
react-router.minimal 87.30 KiB -6 B (-0.01%) 274.68 KiB 75.85 KiB █████████▂▂▁
react-router.full 90.80 KiB +6 B (+0.01%) 286.19 KiB 78.84 KiB ▁▁▁▁▁▁▁▁▁▇▇█
solid-router.minimal 35.55 KiB 0 B (0.00%) 106.95 KiB 31.97 KiB ▁▁▁▁▁████▁▁
solid-router.full 40.27 KiB -6 B (-0.01%) 121.16 KiB 36.27 KiB ▁▄▄▄▄▆███▆▆▆
vue-router.minimal 53.02 KiB -288 B (-0.53%) 150.98 KiB 47.65 KiB ███████████▁
vue-router.full 58.32 KiB -132 B (-0.22%) 167.72 KiB 52.21 KiB ███████████▁
react-start.minimal 101.92 KiB +2 B (+0.00%) 322.84 KiB 88.18 KiB ▁▁▁▁▁▁▁▁▁▆▆█
react-start.full 105.35 KiB +3 B (+0.00%) 333.17 KiB 91.05 KiB █████████▁▁▃
react-start.rsbuild.minimal 101.45 KiB 0 B (0.00%) 325.78 KiB 87.12 KiB ▁▁▁▁▁▁▁▁▁██
react-start.rsbuild.full 104.76 KiB 0 B (0.00%) 336.47 KiB 89.88 KiB ▁▁▁▁▁▁▁▁▁██
solid-start.minimal 49.54 KiB -5 B (-0.01%) 152.77 KiB 43.70 KiB ▁▁▁▁▁████▂▂▁
solid-start.full 55.34 KiB +9 B (+0.02%) 169.67 KiB 48.69 KiB ▁▄▄▄▄▅███▆▆▇

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

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-start-rsc/tests/preinitCssHrefs.test.ts (1)

31-93: Good coverage — consider one additional assertion for HMR skip ordering.

The suite covers the important cases (positive match, negative match, mixed, undefined/empty, ReadonlySet). The toHaveBeenNthCalledWith assertions on lines 67-74 already implicitly verify that cache-busted entries don't shift the call order, so this is solid.

Optional nit: the beforeEach/afterEach mockRestore pattern works, but vi.restoreAllMocks() in a top-level afterEach (or configuring restoreMocks: true in vitest config) would let you drop the preinitSpy variable declaration and the explicit restore. Not a blocker.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-start-rsc/tests/preinitCssHrefs.test.ts` around lines 31 - 93,
Add an explicit assertion that cache-busted HMR-style hrefs are skipped without
affecting the relative ordering of non-cache-busted hrefs: update the mixed-list
test that calls preinitCssHrefs(...) to include an assertion on the exact call
order of ReactDOM.preinit (preinitSpy) for the non-cache-busted entries so order
is validated (or add a new test that supplies interleaved cache-busted and
non-cache-busted hrefs and asserts preinitSpy was called in the original
relative order). Optionally simplify mock cleanup by replacing the
beforeEach/afterEach preinitSpy + preinitSpy.mockRestore() pattern with a single
afterEach(() => vi.restoreAllMocks()) and remove the preinitSpy variable if you
prefer.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/react-start-rsc/tests/preinitCssHrefs.test.ts`:
- Around line 31-93: Add an explicit assertion that cache-busted HMR-style hrefs
are skipped without affecting the relative ordering of non-cache-busted hrefs:
update the mixed-list test that calls preinitCssHrefs(...) to include an
assertion on the exact call order of ReactDOM.preinit (preinitSpy) for the
non-cache-busted entries so order is validated (or add a new test that supplies
interleaved cache-busted and non-cache-busted hrefs and asserts preinitSpy was
called in the original relative order). Optionally simplify mock cleanup by
replacing the beforeEach/afterEach preinitSpy + preinitSpy.mockRestore() pattern
with a single afterEach(() => vi.restoreAllMocks()) and remove the preinitSpy
variable if you prefer.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9fe7e78c-fd22-4676-962f-e436b8c8a27e

📥 Commits

Reviewing files that changed from the base of the PR and between c19571f and 066c658.

📒 Files selected for processing (4)
  • packages/react-start-rsc/src/CompositeComponent.tsx
  • packages/react-start-rsc/src/RscNodeRenderer.tsx
  • packages/react-start-rsc/src/preinitCssHrefs.ts
  • packages/react-start-rsc/tests/preinitCssHrefs.test.ts

@nx-cloud
Copy link
Copy Markdown
Contributor

nx-cloud Bot commented Apr 17, 2026

View your CI Pipeline Execution ↗ for commit 066c658

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

☁️ Nx Cloud last updated this comment at 2026-04-17 23:04:58 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 17, 2026

More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-rsc

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: 066c658

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 17, 2026

Merging this PR will not alter performance

✅ 6 untouched benchmarks


Comparing jantimon:fix-rsc (066c658) with main (b57e898)1

Open in CodSpeed

Footnotes

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

@schiller-manuel
Copy link
Copy Markdown
Contributor

does this still update css then in dev upon hmr? if yes, how?

Comment thread packages/react-start-rsc/src/preinitCssHrefs.ts Outdated
@schiller-manuel
Copy link
Copy Markdown
Contributor

w.r.t. to e2e tests:
we already have hmr e2e tests in e2e/react-start/e2e
a similar setup should work here as well

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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
e2e/react-start/rsc-hmr/tests/rsc-hmr-css.spec.ts (1)

90-110: Add an assertion for the stale stylesheet-link regression.

This second-edit test proves CSS still updates, but not that old cache-busted <link rel="stylesheet"> entries stop accumulating. Please assert the relevant stylesheet link count/hrefs in <head> remain bounded after repeated edits so the test fails on the original leak.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/react-start/rsc-hmr/tests/rsc-hmr-css.spec.ts` around lines 90 - 110,
After the two editCss steps in the test `${c.label}: a second css change in the
same file hot-updates the style`, add assertions that the document head does not
accumulate stale cache-busted stylesheet links: query the head for <link
rel="stylesheet"> elements (via page.evaluate or page.locator) and assert the
count remains bounded (e.g., equals the expected number) and that their hrefs
are the current/expected href(s) rather than many old cache-busted variants;
place these checks after the final
expect(page.getByTestId(c.titleTestId)).toHaveCSS(...) so the test fails if
stale `<link rel="stylesheet">` entries leak after calling editCss twice. Ensure
you reference the same c/titleTestId context as used in this test and use page.*
helpers (page.evaluate or page.locator) to inspect head links.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/react-start/rsc-hmr/package.json`:
- Line 4: The package.json currently declares "sideEffects": false which causes
Vite/webpack to tree-shake CSS imports used by GlobalCssCard.tsx and
CssModulesCard.tsx; update package.json to preserve CSS side-effects by
replacing the boolean with an array of patterns (e.g., ["*.css", "**/*.css"] or
the specific CSS module paths) or set "sideEffects": true so CSS imports are not
removed during build, ensuring GlobalCssCard.tsx and CssModulesCard.tsx CSS
remains included.

In `@e2e/react-start/rsc-hmr/tests/hydration.ts`:
- Line 1: The import currently mixes a value import and an inline type specifier
("expect, type Page from '@playwright/test'"), which ESLint flags; change it to
two imports: keep the value import for "expect" from '@playwright/test' and add
a separate type-only import for "Page" (e.g., "import type { Page } from
'@playwright/test'") so the "expect" and "Page" symbols are imported separately
and satisfy member ordering and type-import rules.

In `@e2e/react-start/rsc-hmr/tests/rsc-hmr-css.spec.ts`:
- Around line 1-5: Reorder the import statements so Node built-ins come before
package imports: move "import { readFile, writeFile } from 'node:fs/promises'"
and "import path from 'node:path'" above the third‑party imports (e.g., "import
{ expect } from '@playwright/test'" and "import { test } from
'@tanstack/router-e2e-utils'"), keeping the local relative import "import {
waitForHydration } from './hydration'" last; preserve the same named symbols
(expect, test, readFile, writeFile, path, waitForHydration) and their usage.

---

Nitpick comments:
In `@e2e/react-start/rsc-hmr/tests/rsc-hmr-css.spec.ts`:
- Around line 90-110: After the two editCss steps in the test `${c.label}: a
second css change in the same file hot-updates the style`, add assertions that
the document head does not accumulate stale cache-busted stylesheet links: query
the head for <link rel="stylesheet"> elements (via page.evaluate or
page.locator) and assert the count remains bounded (e.g., equals the expected
number) and that their hrefs are the current/expected href(s) rather than many
old cache-busted variants; place these checks after the final
expect(page.getByTestId(c.titleTestId)).toHaveCSS(...) so the test fails if
stale `<link rel="stylesheet">` entries leak after calling editCss twice. Ensure
you reference the same c/titleTestId context as used in this test and use page.*
helpers (page.evaluate or page.locator) to inspect head links.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 799e91c2-3377-4117-b130-eb55afc63c02

📥 Commits

Reviewing files that changed from the base of the PR and between 066c658 and 94b1643.

📒 Files selected for processing (23)
  • e2e/react-start/rsc-hmr/.gitignore
  • e2e/react-start/rsc-hmr/eslint.config.js
  • e2e/react-start/rsc-hmr/package.json
  • e2e/react-start/rsc-hmr/playwright.config.ts
  • e2e/react-start/rsc-hmr/src/routeTree.gen.ts
  • e2e/react-start/rsc-hmr/src/router.tsx
  • e2e/react-start/rsc-hmr/src/routes/__root.tsx
  • e2e/react-start/rsc-hmr/src/routes/index.tsx
  • e2e/react-start/rsc-hmr/src/routes/rsc-hmr-css-modules.tsx
  • e2e/react-start/rsc-hmr/src/routes/rsc-hmr-global-css.tsx
  • e2e/react-start/rsc-hmr/src/utils/CssModulesCard.module.css
  • e2e/react-start/rsc-hmr/src/utils/CssModulesCard.tsx
  • e2e/react-start/rsc-hmr/src/utils/GlobalCssCard.css
  • e2e/react-start/rsc-hmr/src/utils/GlobalCssCard.tsx
  • e2e/react-start/rsc-hmr/src/utils/cssModulesCardServerComponent.tsx
  • e2e/react-start/rsc-hmr/src/utils/globalCssCardServerComponent.tsx
  • e2e/react-start/rsc-hmr/tests/hydration.ts
  • e2e/react-start/rsc-hmr/tests/rsc-hmr-css.spec.ts
  • e2e/react-start/rsc-hmr/tests/setup/global.setup.ts
  • e2e/react-start/rsc-hmr/tests/setup/global.teardown.ts
  • e2e/react-start/rsc-hmr/tsconfig.json
  • e2e/react-start/rsc-hmr/vite.config.ts
  • packages/react-start-rsc/src/preinitCssHrefs.ts
✅ Files skipped from review due to trivial changes (7)
  • e2e/react-start/rsc-hmr/.gitignore
  • e2e/react-start/rsc-hmr/src/utils/GlobalCssCard.css
  • e2e/react-start/rsc-hmr/src/utils/CssModulesCard.module.css
  • e2e/react-start/rsc-hmr/tsconfig.json
  • e2e/react-start/rsc-hmr/src/utils/GlobalCssCard.tsx
  • e2e/react-start/rsc-hmr/eslint.config.js
  • e2e/react-start/rsc-hmr/src/routeTree.gen.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react-start-rsc/src/preinitCssHrefs.ts

Comment thread e2e/react-start/rsc-hmr/package.json Outdated
Comment thread e2e/react-start/rsc-hmr/tests/hydration.ts Outdated
Comment thread e2e/react-start/rsc-hmr/tests/rsc-hmr-css.spec.ts Outdated
@jantimon
Copy link
Copy Markdown
Author

does this still update css then in dev upon hmr? if yes, how?

@vitejs/plugin-rsc <Resources> emits a <link rel="stylesheet" data-rsc-css-href="..."> directly in the Flight stream for every CSS dependency

preinit was adding a second <link> for preloading

@jantimon
Copy link
Copy Markdown
Author

The HMR e2e tests in this branch are blocked by vitejs/vite-plugin-react#1188

@hi-ogawa could you please take a look at both PRs and let me know what you think about the fix and the e2e tests?

@schiller-manuel
Copy link
Copy Markdown
Contributor

@jantimon can you please test with the vite-rsc-plugin preview build in vitejs/vite-plugin-react#1188 (comment)

@jantimon
Copy link
Copy Markdown
Author

@hi-ogawa did a great job this weekend and finished, merged and relased my vite-plugin-react pr:
vite-plugin-react changelog 🎉

vite-plugin-react v0.5.25

rsc: fix server css hmr with cssLinkPrecedence: false (#1188) (f4647c4)

I will retest tanstack start again with this version and try to simplify the e2e tests today

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/react-start/rsc-hmr/tests/rsc-hmr-css.spec.ts`:
- Around line 71-132: Add an assertion in each affected test after the CSS edit
and final expect to verify the set/count of HMR stylesheet links to catch the
stale-link regression: query the DOM for
link[rel="stylesheet"][data-rsc-css-href] (e.g., via page.locator or
page.evaluate) and assert that there is exactly one entry (or that the href set
contains only the latest href) so that tests like the ones using editCss,
waitForHydration, and c.titleTestId fail if old <link> nodes accumulate; place
this check right after the final await expect(...) in each test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f49d6cfa-c721-44d3-89e3-24fe216ab5d7

📥 Commits

Reviewing files that changed from the base of the PR and between 3fa7ef9 and 1d97f83.

📒 Files selected for processing (27)
  • .changeset/fix-rsc-stale-css-hmr.md
  • e2e/react-start/rsc-hmr/.gitignore
  • e2e/react-start/rsc-hmr/eslint.config.js
  • e2e/react-start/rsc-hmr/package.json
  • e2e/react-start/rsc-hmr/playwright.config.ts
  • e2e/react-start/rsc-hmr/src/routeTree.gen.ts
  • e2e/react-start/rsc-hmr/src/router.tsx
  • e2e/react-start/rsc-hmr/src/routes/__root.tsx
  • e2e/react-start/rsc-hmr/src/routes/index.tsx
  • e2e/react-start/rsc-hmr/src/routes/rsc-hmr-css-modules.tsx
  • e2e/react-start/rsc-hmr/src/routes/rsc-hmr-global-css.tsx
  • e2e/react-start/rsc-hmr/src/utils/CssModulesCard.module.css
  • e2e/react-start/rsc-hmr/src/utils/CssModulesCard.tsx
  • e2e/react-start/rsc-hmr/src/utils/GlobalCssCard.css
  • e2e/react-start/rsc-hmr/src/utils/GlobalCssCard.tsx
  • e2e/react-start/rsc-hmr/src/utils/cssModulesCardServerComponent.tsx
  • e2e/react-start/rsc-hmr/src/utils/globalCssCardServerComponent.tsx
  • e2e/react-start/rsc-hmr/tests/hydration.ts
  • e2e/react-start/rsc-hmr/tests/rsc-hmr-css.spec.ts
  • e2e/react-start/rsc-hmr/tests/setup/global.setup.ts
  • e2e/react-start/rsc-hmr/tests/setup/global.teardown.ts
  • e2e/react-start/rsc-hmr/tsconfig.json
  • e2e/react-start/rsc-hmr/vite.config.ts
  • package.json
  • packages/react-start-rsc/src/CompositeComponent.tsx
  • packages/react-start-rsc/src/RscNodeRenderer.tsx
  • packages/react-start-rsc/src/preinitCssHrefs.ts
✅ Files skipped from review due to trivial changes (15)
  • e2e/react-start/rsc-hmr/.gitignore
  • package.json
  • .changeset/fix-rsc-stale-css-hmr.md
  • e2e/react-start/rsc-hmr/src/utils/cssModulesCardServerComponent.tsx
  • e2e/react-start/rsc-hmr/src/utils/GlobalCssCard.css
  • e2e/react-start/rsc-hmr/src/utils/GlobalCssCard.tsx
  • e2e/react-start/rsc-hmr/src/utils/CssModulesCard.module.css
  • e2e/react-start/rsc-hmr/src/utils/CssModulesCard.tsx
  • e2e/react-start/rsc-hmr/tests/hydration.ts
  • e2e/react-start/rsc-hmr/src/router.tsx
  • e2e/react-start/rsc-hmr/src/routes/rsc-hmr-css-modules.tsx
  • e2e/react-start/rsc-hmr/tsconfig.json
  • e2e/react-start/rsc-hmr/eslint.config.js
  • e2e/react-start/rsc-hmr/package.json
  • e2e/react-start/rsc-hmr/src/routeTree.gen.ts
🚧 Files skipped from review as they are similar to previous changes (10)
  • packages/react-start-rsc/src/CompositeComponent.tsx
  • e2e/react-start/rsc-hmr/tests/setup/global.teardown.ts
  • e2e/react-start/rsc-hmr/src/utils/globalCssCardServerComponent.tsx
  • e2e/react-start/rsc-hmr/src/routes/index.tsx
  • e2e/react-start/rsc-hmr/vite.config.ts
  • e2e/react-start/rsc-hmr/tests/setup/global.setup.ts
  • e2e/react-start/rsc-hmr/playwright.config.ts
  • packages/react-start-rsc/src/RscNodeRenderer.tsx
  • e2e/react-start/rsc-hmr/src/routes/rsc-hmr-global-css.tsx
  • e2e/react-start/rsc-hmr/src/routes/__root.tsx

Comment thread e2e/react-start/rsc-hmr/tests/rsc-hmr-css.spec.ts
@jantimon
Copy link
Copy Markdown
Author

Okay so I verified that @vitejs/plugin-rsc 0.5.25 + vite 8.0.10 + this PR fixes the HMR and all new e2e tests pass

I also verified this PR is still needed

Removing if (process.env.NODE_ENV !== 'production') return in https://github.com/jantimon/tanstack-router/blob/1d97f8323f4f686b357530ea0fe7dc37dc1bee83/packages/react-start-rsc/src/preinitCssHrefs.ts#L11 will break the e2e test which verifies that no stale styles survive a delete:

 tests/rsc-hmr-css.spec.ts:113:5 › rsc css hmr › global .css: removing a css property hot-updates the style
 Expected: "none"
 Received: "uppercase"

@jantimon
Copy link
Copy Markdown
Author

w.r.t. to e2e tests: we already have hmr e2e tests in e2e/react-start/e2e a similar setup should work here as well

I looked into consolidating these tests but there are some blockers because of rspack rsc compatibility:

  1. The following import fails because there is no @rspack/core/rsc/ssr:
    import { setOnClientReference } from '@rspack/core/rsc/ssr'
  2. If we exclude the rsc routes for rspack routeTree.gen.ts flip-flops between toolchains
  3. Types don't match for rspack and rsc so I ran into:
    src/routes/rsc-hmr-css-modules.tsx(4,38): error TS2345: Argument of type '"/rsc-hmr-css-modules"' is not assignable to parameter of type 'keyof FileRoutesByPath | undefined'
    src/routes/rsc-hmr-css-modules.tsx(13,11): error TS2339: Property 'Server' does not exist on type '{ crumb: string; } | undefined'

If you like I can disable the typecheck for rspack inside e2e/react-start/rsc-hmr, exclude the HMR routes and add a git ignore entry for routeTree.gen.ts

What do you think?

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