voidzero-dev / voidzero-dev/vite-plus

Cloudflare workerd SSR: React module duplication in HeadContent useContext when used with TanStack Start + @cloudflare/vite-plugin

Open
#1,671 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
5.8k
Forks
262
Avg merge
1d 34m
Merged PRs (30d)
135

Description

## Setup

- `vite-plus` (alias `vite` → `@voidzero-dev/vite-plus-core`) `0.1.22`
- `@cloudflare/vite-plugin` `^1.37.2` (workerd SSR environment)
- `@tanstack/react-start` `^1.168.10` + `@tanstack/react-router` `^1.170.7`
- React 19, `@vitejs/plugin-react` `^6.0.2`
- bun workspaces, single app uses TanStack Start

`vite.config.ts` mirrors the working garden setup exactly (vanilla Vite 8 + same `environments.ssr.optimizeDeps.include` list + same plugin order):

\`\`\`ts
export default defineConfig({
resolve: { tsconfigPaths: true, dedupe: ["react", "react-dom"] },
environments: {
ssr: {
optimizeDeps: {
include: [
"react", "react/jsx-runtime", "react/jsx-dev-runtime",
"react-dom", "react-dom/server",
"@tanstack/react-router > @tanstack/react-store",
],
},
},
},
plugins: [
cloudflare({ viteEnvironment: { name: "ssr" } }),
agents(),
tailwindcss(),
tanstackStart(),
viteReact(),
],
});
\`\`\`

## What happens

SSR shell render of \`HeadContent\` crashes with React invalid-hook-call:

\`\`\`
TypeError: Cannot read properties of null (reading 'useContext')
at exports.useContext (deps_ssr/react.js?v=…:706)
at useRouter (@tanstack/react-router/src/useRouter.tsx:20)
at useTags (@tanstack/react-router/src/headContentUtils.tsx:213)
at HeadContent (@tanstack/react-router/src/HeadContent.dev.tsx:22)
\`\`\`

i.e. the classic two-copies-of-React-in-workerd-SSR situation that cloudflare/workers-sdk#11825 describes. Browser ends up on the JSON 500 page (\`{"status":500,"message":"HTTPError"}\`).

The exact same source tree + the exact same config, run on **vanilla Vite 8** in another project (https://github.com/voidzero-dev/garden parallel — its \`apps/web\` works fine), renders cleanly.

## Comparison

| | vanilla Vite 8 (garden) | vite-plus-core 0.1.22 (this repo) |
|---|---|---|
| Same vite.config | works | 500 with useContext null in HeadContent |
| Same plugin order | works | broken |
| Same SSR optimizeDeps.include | works | broken |

Switching this repo's catalog alias to vanilla Vite 8 doesn't fully fix it either — first \`/\` render works, but on any subsequent SSR render of the route (HMR reload, client navigate, etc.) the same useContext null surfaces.

## Suspicion

`@voidzero-dev/vite-plus-core` ships its own (rolldown-backed?) Vite that handles \`environments.ssr.optimizeDeps\` differently from upstream Vite's dep optimizer, so the React module the workerd runner ends up with isn't the same instance the route bundle imports. This trips every provider hook downstream (`useContext` in `HeadContent`, `useEffect` in `QueryClientProvider`, etc.).

Repro repo: I can put one up if helpful — but the diff against the existing TanStack Start cloudflare example is essentially "swap upstream Vite for the vite-plus-core alias and add `@cloudflare/vite-plugin`".

## Related

- https://github.com/cloudflare/workers-sdk/issues/11825
- https://github.com/TanStack/router/issues/5208

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.