Skip to content

Inconsistent hydration: some tags in the head are duplicated, eg. application/ld+json #6569

Closed

Description

@kcoet

Which project does this relate to?

Start

Describe the bug

Some tags appear to be duplicated during hydration, even though the server has sent them correctly if we look at them with view-source:[url]

Image

Your Example Website or App

https://stackblitz.com/edit/github-zpekad3w?file=src%2Froutes%2F__root.tsx

Steps to Reproduce the Bug or Issue

  1. run npm run build
  2. start production server npm run start
  3. open production URL
  4. check elements tab on browser dev tools, the see application/ld+json script

It looks like there are 2 even though I only inserted 1. The duplicate tag appears without content.

Expected behavior

Hydration should produce matching elements according to the head content sent by the server.

Screenshots or Videos

No response

Platform

  • Router / Start Version: 1.157.18
  • OS: Linux
  • Browser: Chrome
  • Browser Version: 144.0.7559.110
  • Bundler: vite
  • Bundler Version: 7.3.1

Additional context

No response

Activity

  1. Ammar123890 commented on Feb 8, 2026

    @Ammar123890

    I'm seeing this exact same behavior in React Router v7, and looking at the source here in TanStack Router, it seems to share the same root cause.

    In packages/router/src/HeadContent.tsx (specifically around the Script or Meta component rendering logic), the framework uses the stringified content or a simple index as the React key during iteration.

    When using a CDN like Cloudflare (Auto-Minify) or when dealing with special character encoding (e.g., Arabic/Unicode), the server-rendered string often differs slightly from the client-hydrated string:

    Server: <script>...{"foo":"bar"}...</script> (Minified/Encoded)
    Client: <script>...{ "foo": "bar" }...</script> (Standard formatting)

    React sees Key(Server) !== Key(Client), assumes the server node is foreign, leaves it in the DOM (the duplicate), and appends a new one.

    We bypassed the framework's head management entirely for JSON-LD and moved the <script> tag directly into the Component Body

    This forces React to manage the script lifecycle natively as part of the component tree, completely avoiding the hydration mismatch in the <head>.

  2. AlexandreSi commented on Feb 9, 2026

    @AlexandreSi

    Hi,
    I have a similar issue: I have the empty script tag and nothing inside, but the correct ld+json tag is nowhere to be found.

  3. Ammar123890 commented on Feb 10, 2026

    @Ammar123890

    @AlexandreSi
    Can you share some snapshots of your code?

  4. AlexandreSi commented on Feb 10, 2026

    @AlexandreSi

    @AlexandreSi Can you share some snapshots of your code?

    Hi @Ammar123890,

    Thank you for your response.
    I created a new issue with all the details here: #6627

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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