TanStack / TanStack/devtools

data-tsd-source inject-source plugin causes hydration mismatch in SSR (TanStack Start)

Open Beginner friendly
#405 0 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
499
Forks
100
Avg merge
1d 17h
Merged PRs (30d)
4

Description

Description

The inject-source plugin in @tanstack/devtools-vite causes React hydration mismatch warnings in TanStack Start (SSR) projects. The data-tsd-source attributes are injected with different line numbers on the server vs client transform passes, because other Vite plugins (e.g. tanstackStart(), viteReact()) shift line numbers differently between SSR and client bundles.

Reproduction

  1. Create a TanStack Start project with @tanstack/devtools-vite v0.6.0
  2. Add devtools() to vite.config.ts
  3. Navigate to any SSR-rendered route
  4. Observe console warning:
A tree hydrated but some attributes of the server rendered HTML didn't match the client properties.

<div
+ data-tsd-source="/src/routes/simulations/$slug/editor.tsx:632:5"
- data-tsd-source="/src/routes/simulations/$slug/editor.tsx:636:5"
>

Line numbers are consistently off by a fixed offset (4 lines in my case) — the server and client transforms produce different source locations for the same JSX elements.

Root Cause

In plugin.ts, the inject-source Vite plugin's apply function only checks config.mode === 'development':

apply(config) {
  return config.mode === 'development' && injectSourceConfig.enabled
},

The transform hook does not check the ssr boolean that Vite passes as the third argument. Since data-tsd-source is only useful on the client (for click-to-source in the devtools UI), injecting it during the SSR pass creates the mismatch.

Suggested Fix

Skip source injection during the SSR transform pass. The Vite transform hook receives { ssr: boolean } as the third argument:

transform(code, id, options) {
  if (options?.ssr) return; // Only inject on client
  // ... existing Babel transform
}

This would eliminate the hydration mismatch while preserving click-to-source functionality on the client.

Workaround

Disable source injection entirely:

devtools({ injectSource: { enabled: false } })

This removes the hydration warning but loses click-to-source in the devtools UI.

Environment

  • @tanstack/devtools-vite: 0.6.0
  • @tanstack/react-start: latest
  • React 19
  • Vite 7

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in plugin.ts and inspect the inject-source Vite plugin's transform hook, including the ssr option passed by Vite. Skip source injection for SSR transforms while preserving client-side injection, then verify that the reported hydration mismatch no longer occurs and click-to-source remains available.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
devtools
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.