Summary
Built-in file-system routing (the experimental fsRoutes option, added in #123) only renders in vite dev when ssr: true is set. With the default ssr: false, the dev server renders the app shell but the page content never appears. Production builds (vite build) are unaffected and work with ssr either way.
We currently work around this by requiring/documenting ssr: true for fs-routing. This issue tracks lifting that limitation so ssr: false works in dev too.
Root cause
fs-routing renders pages through FUNSTACK Router's <Router> (a client component) and passes the page components as server-component route data:
<Router routes={[{ path: "/about", component: <AboutPage /> }]} ssr={{ path }} />
-
In a production build (ssr: false), the RSC payload is pre-rendered at build time, so the server components in routes are rendered to their output and serialized. ✅
-
In vite dev with ssr: false, the app is rendered on the client from the RSC stream. @vitejs/plugin-rsc serializes the server-component route data as an eval'd dev-JSX source reference (a $E…function AboutPage(){ _jsxDEV(...) } entry in the flight data). When the browser evaluates it, the jsxDEV import resolves to the client's optimized react/jsx-runtime, which doesn't export it:
SyntaxError: The requested module '/node_modules/.vite/deps/react_jsx-runtime.js?v=…'
does not provide an export named 't'
React's error boundary then renders the "Unexpected Error" shell, so no page content appears.
-
In vite dev with ssr: true, pages are rendered on the server, so the broken client eval path is never taken. ✅
This is not specific to the fsRoutes codegen — the same failure reproduces with the equivalent hand-written userland pattern (module-level routes with component: <Page />, ssr: false). It's an interaction between "server components passed as data to a client component" and @vitejs/plugin-rsc's dev serialization.
Reproduction
- Configure
fsRoutes (or a hand-written <Router routes={[{ component: <ServerPage /> }]} />) without ssr: true.
vite dev, open any route in a browser.
- The shell loads but page content is missing; the console shows the
react/jsx-runtime jsxDEV/'t' export error above.
packages/static/e2e/tests-dev/fs-routing.spec.ts reproduces this when the fixture's ssr is false.
Possible directions
- Render the matched route on the server even when
ssr: false in dev (mirror the production pre-render path), so the client receives rendered output rather than an eval'd server-component reference.
- Investigate whether
@vitejs/plugin-rsc can serialize server components passed as client-component props consistently between dev and build (upstream fix or configuration).
- Align the dev jsx runtime so the eval'd dev-JSX (
jsxDEV) resolves correctly on the client.
Workaround (current)
Set ssr: true when using fsRoutes. This is documented in the File-System Routing guide and used by the example and e2e fixture.
Related: #123
Summary
Built-in file-system routing (the experimental
fsRoutesoption, added in #123) only renders invite devwhenssr: trueis set. With the defaultssr: false, the dev server renders the app shell but the page content never appears. Production builds (vite build) are unaffected and work withssreither way.We currently work around this by requiring/documenting
ssr: truefor fs-routing. This issue tracks lifting that limitation sossr: falseworks in dev too.Root cause
fs-routing renders pages through FUNSTACK Router's
<Router>(a client component) and passes the page components as server-component route data:In a production build (
ssr: false), the RSC payload is pre-rendered at build time, so the server components inroutesare rendered to their output and serialized. ✅In
vite devwithssr: false, the app is rendered on the client from the RSC stream.@vitejs/plugin-rscserializes the server-component route data as an eval'd dev-JSX source reference (a$E…function AboutPage(){ _jsxDEV(...) }entry in the flight data). When the browser evaluates it, thejsxDEVimport resolves to the client's optimizedreact/jsx-runtime, which doesn't export it:React's error boundary then renders the "Unexpected Error" shell, so no page content appears.
In
vite devwithssr: true, pages are rendered on the server, so the broken client eval path is never taken. ✅This is not specific to the
fsRoutescodegen — the same failure reproduces with the equivalent hand-written userland pattern (module-levelrouteswithcomponent: <Page />,ssr: false). It's an interaction between "server components passed as data to a client component" and@vitejs/plugin-rsc's dev serialization.Reproduction
fsRoutes(or a hand-written<Router routes={[{ component: <ServerPage /> }]} />) withoutssr: true.vite dev, open any route in a browser.react/jsx-runtimejsxDEV/'t'export error above.packages/static/e2e/tests-dev/fs-routing.spec.tsreproduces this when the fixture'sssrisfalse.Possible directions
ssr: falsein dev (mirror the production pre-render path), so the client receives rendered output rather than an eval'd server-component reference.@vitejs/plugin-rsccan serialize server components passed as client-component props consistently between dev and build (upstream fix or configuration).jsxDEV) resolves correctly on the client.Workaround (current)
Set
ssr: truewhen usingfsRoutes. This is documented in the File-System Routing guide and used by the example and e2e fixture.Related: #123