Skip to content

TanStack Start + Cloudflare Workers — duplicate React instances in SSR dev environment cause "Invalid hook call" #7119

@jasongitmail

Description

@jasongitmail

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:

  1. node_modules/.vite/deps_ssr/react.js (pre-bundled, used by react-dom/server)
  2. 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

  1. Create a TanStack Start project with Cloudflare Workers deployment (@cloudflare/vite-plugin)
  2. Ensure vite.config.ts uses cloudflare({ viteEnvironment: { name: "ssr" } }) alongside tanstackStart()
  3. Run the dev server (vite dev)
  4. Navigate to any route that uses React hooks (e.g., useState, useEffect)
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions