Which project does this relate to?
Start
Describe the bug
In RSC mode, a route component does not Hot Module Reload when its route file also contains a co-located createServerFn (e.g. a loader server function in the same file as the route component).
On editing the route component's JSX, the terminal shows only:
(rsc) hmr update /src/routes/index.tsx?tss-serverfn-split
…with no client update, so the DOM never changes — and there is no full reload either. Moving the createServerFn into a separate file restores Fast Refresh, which is the tell that this is specifically about co-location.
I've traced the root cause to @vitejs/plugin-rsc and opened a proposed fix upstream: vitejs/vite-plugin-react#1248. Filing here for visibility/tracking (so Start users hitting "my route won't HMR" find the cause + workaround, and so the @vitejs/plugin-rsc bump is tracked once it lands).
Root cause (short version): the route file's createServerFn puts the file in the rsc module graph. @vitejs/plugin-rsc's client hotUpdate returns [] for any file in the rsc graph that isn't inside a "use client" boundary (a guard to avoid full reloads from server-only files watched as style deps, e.g. Tailwind/addWatchFile). The route component is a genuine client module (imported by the client route tree) but isn't a "use client" reference, so its client HMR is wrongly suppressed.
Workaround: move the createServerFn/server code into a separate (e.g. *.server.ts) module and import it into the route file.
Complete minimal reproducer
https://github.com/sreetamdas/tanstack-rsc-hmr-co-located-serverfn-repro
Steps to Reproduce the Bug
pnpm install && pnpm dev and open http://localhost:3000.
- Click Increment once (so
Count: 1) to seed client state.
- In
src/routes/index.tsx, edit the <h1 data-testid="marker"> text (edit-me-baseline → anything) and save.
- Observe: the heading does not update; the terminal logs only
(rsc) hmr update …?tss-serverfn-split; no full reload.
- For contrast: move
getServerData into a separate file and import it — HMR now works (heading updates, counter preserved).
Expected behavior
Editing a route component's JSX should Fast Refresh the component in dev, regardless of whether the route file also defines a createServerFn. Server-only code co-located in the file should not strip the route component's client HMR boundary.
Platform
- Router / Start Version:
@tanstack/react-start ^1.168.25, @tanstack/react-router ^1.170.15
- OS: macOS
- Browser: Chrome
- Bundler: vite ^8
- Bundler plugin:
@vitejs/plugin-rsc 0.5.27
Additional context
Proposed upstream fix (with full analysis): vitejs/vite-plugin-react#1248 — only suppress the client HMR when the file has no non-CSS client importers (mirroring the existing rsc-branch's importers.every(isCSSRequest) check), so genuine client modules co-located with rsc-graph code keep Fast Refresh.
Which project does this relate to?
Start
Describe the bug
In RSC mode, a route component does not Hot Module Reload when its route file also contains a co-located
createServerFn(e.g. a loader server function in the same file as the route component).On editing the route component's JSX, the terminal shows only:
…with no client update, so the DOM never changes — and there is no full reload either. Moving the
createServerFninto a separate file restores Fast Refresh, which is the tell that this is specifically about co-location.I've traced the root cause to
@vitejs/plugin-rscand opened a proposed fix upstream: vitejs/vite-plugin-react#1248. Filing here for visibility/tracking (so Start users hitting "my route won't HMR" find the cause + workaround, and so the@vitejs/plugin-rscbump is tracked once it lands).Root cause (short version): the route file's
createServerFnputs the file in therscmodule graph.@vitejs/plugin-rsc's clienthotUpdatereturns[]for any file in the rsc graph that isn't inside a"use client"boundary (a guard to avoid full reloads from server-only files watched as style deps, e.g. Tailwind/addWatchFile). The route component is a genuine client module (imported by the client route tree) but isn't a"use client"reference, so its client HMR is wrongly suppressed.Workaround: move the
createServerFn/server code into a separate (e.g.*.server.ts) module and import it into the route file.Complete minimal reproducer
https://github.com/sreetamdas/tanstack-rsc-hmr-co-located-serverfn-repro
Steps to Reproduce the Bug
pnpm install && pnpm devand open http://localhost:3000.Count: 1) to seed client state.src/routes/index.tsx, edit the<h1 data-testid="marker">text (edit-me-baseline→ anything) and save.(rsc) hmr update …?tss-serverfn-split; no full reload.getServerDatainto a separate file and import it — HMR now works (heading updates, counter preserved).Expected behavior
Editing a route component's JSX should Fast Refresh the component in dev, regardless of whether the route file also defines a
createServerFn. Server-only code co-located in the file should not strip the route component's client HMR boundary.Platform
@tanstack/react-start^1.168.25,@tanstack/react-router^1.170.15@vitejs/plugin-rsc0.5.27Additional context
Proposed upstream fix (with full analysis): vitejs/vite-plugin-react#1248 — only suppress the client HMR when the file has no non-CSS client importers (mirroring the existing rsc-branch's
importers.every(isCSSRequest)check), so genuine client modules co-located with rsc-graph code keep Fast Refresh.