Which project does this relate to?
Start
Describe the bug
When using TanStack Start with @cloudflare/vite-plugin for Cloudflare Workers deployment, the SSR dev environment resolves React through two different paths, creating two separate ReactSharedInternals objects. This causes the classic "Invalid hook call" error because one instance has H = null while the other has the actual dispatcher.
The root cause is that TanStack Start's Vite plugin (tanstackStart()) only adds React to environments.ssr.optimizeDeps conditionally — it checks whether optimizeDeps.noDiscovery === false. In the Cloudflare Workers dev environment, this condition isn't met, so React is not pre-bundled into the SSR dep cache. Vite then resolves react via two different paths:
node_modules/.vite/deps_ssr/react.js (pre-bundled, used by react-dom/server)
- A direct
node_modules/react/ resolution (used by application source files)
These are two distinct module instances with separate ReactSharedInternals, so hooks set up by one are invisible to the other.
Workaround
Manually adding environments.ssr.optimizeDeps.include in vite.config.ts fixes the issue:
environments: {
ssr: {
optimizeDeps: {
include: [
"react-dom/server"
],
},
},
},
Suggested fix
TanStack Start's Vite plugin should unconditionally include React (and its subpath exports) in environments.ssr.optimizeDeps.include when the Cloudflare adapter/plugin is present, or ideally always — there's no downside to ensuring a single React instance in SSR.
(Issue identified and written with Opus 4.6)
Your Example Website or App
will add if I get time
Steps to Reproduce the Bug or Issue
- Create a TanStack Start project with Cloudflare Workers deployment (
@cloudflare/vite-plugin)
- Ensure
vite.config.ts uses cloudflare({ viteEnvironment: { name: "ssr" } }) alongside tanstackStart()
- Run the dev server (
vite dev)
- Navigate to any route that uses React hooks (e.g.,
useState, useEffect)
- Observe the "Invalid hook call" error in the browser console / SSR error output
The error looks like:
Warning: Invalid hook call. Hooks can only be called inside of the body of a function component.
Inspecting ReactSharedInternals shows H = null on one instance while the dispatcher is set on the other.
Expected behavior
As a user, I expected the dev server to resolve a single React module instance across all SSR code paths, so hooks work correctly. Instead, the Cloudflare Workers SSR environment resolves two separate React instances, breaking all hook calls.
Screenshots or Videos
No response
Platform
- TanStack Start: 1.145.3
- TanStack Router: 1.144.0
- Vite: 7.3.1
- @cloudflare/vite-plugin: 1.13.7
- React: 19.1.0
- OS: macOS
- Browser: Chrome
- Bundler: Vite 7.3.1
Additional context
- The
resolve.dedupe: ["react", "react-dom"] config (which fixes the classic client-side duplicate React issue) does not fix this — it doesn't apply to the environments.ssr resolution.
- Clearing
node_modules/.vite/deps_ssr is also necessary if a stale cache was built before adding the workaround.
- This may also affect other non-Node SSR environments (Deno, Bun workers) that use Vite's environment API, though I've only confirmed it with Cloudflare Workers.
Which project does this relate to?
Start
Describe the bug
When using TanStack Start with
@cloudflare/vite-pluginfor Cloudflare Workers deployment, the SSR dev environment resolves React through two different paths, creating two separateReactSharedInternalsobjects. This causes the classic "Invalid hook call" error because one instance hasH = nullwhile the other has the actual dispatcher.The root cause is that TanStack Start's Vite plugin (
tanstackStart()) only adds React toenvironments.ssr.optimizeDepsconditionally — it checks whetheroptimizeDeps.noDiscovery === false. In the Cloudflare Workers dev environment, this condition isn't met, so React is not pre-bundled into the SSR dep cache. Vite then resolvesreactvia two different paths:node_modules/.vite/deps_ssr/react.js(pre-bundled, used byreact-dom/server)node_modules/react/resolution (used by application source files)These are two distinct module instances with separate
ReactSharedInternals, so hooks set up by one are invisible to the other.Workaround
Manually adding
environments.ssr.optimizeDeps.includeinvite.config.tsfixes the issue:Suggested fix
TanStack Start's Vite plugin should unconditionally include React (and its subpath exports) in
environments.ssr.optimizeDeps.includewhen the Cloudflare adapter/plugin is present, or ideally always — there's no downside to ensuring a single React instance in SSR.(Issue identified and written with Opus 4.6)
Your Example Website or App
will add if I get time
Steps to Reproduce the Bug or Issue
@cloudflare/vite-plugin)vite.config.tsusescloudflare({ viteEnvironment: { name: "ssr" } })alongsidetanstackStart()vite dev)useState,useEffect)The error looks like:
Inspecting
ReactSharedInternalsshowsH = nullon one instance while the dispatcher is set on the other.Expected behavior
As a user, I expected the dev server to resolve a single React module instance across all SSR code paths, so hooks work correctly. Instead, the Cloudflare Workers SSR environment resolves two separate React instances, breaking all hook calls.
Screenshots or Videos
No response
Platform
Additional context
resolve.dedupe: ["react", "react-dom"]config (which fixes the classic client-side duplicate React issue) does not fix this — it doesn't apply to theenvironments.ssrresolution.node_modules/.vite/deps_ssris also necessary if a stale cache was built before adding the workaround.