Fixing Meta Tags Overwritten During Hydration

A server-rendered page can ship the correct <title> and meta description, then lose them a moment later: during hydration the client component mounts with default metadata — before the route’s data resolves — and overwrites the server’s tags. If a crawler captures the DOM in that window, it indexes the defaults. This is a hydration-timing bug, and it is the core failure that avoiding metadata hydration pitfalls addresses.

Step-by-step fix

  1. Confirm the overwrite. Compare the head in the server response with the head after hydration.

    // In the console right after load:
    document.title // is it the route's title, or a default like "My App"?
  2. Hydrate metadata from the same data the server used. The client’s first render must compute the same title/description, so hydration is a no-op for the head.

    // ❌ Client mounts with a default, then updates after fetch → overwrite + flash
    useEffect(() => { document.title = data?.title ?? 'My App'; }, [data]);
    
    // ✅ Title derived from data present at first render (passed from the server)
    function ProductMeta({ product }) {
      useServerInsertedHead(() => <title>{product.name}</title>);
      return null;
    }
  3. Do not render placeholder meta on the first client pass. If data is not ready, render nothing for the head rather than a default that overwrites the server value.

  4. Mutate, don’t replace, the tags so hydration reconciles cleanly instead of tearing down and rebuilding the head.

How hydration overwrites server meta tags The server ships the correct title, hydration mounts default metadata and overwrites it, and the real title only returns after data resolves — leaving a window where a crawler indexes the default. The server ships the right meta — hydration overwrites it Server render title = Product Name correct Hydration mounts title = default (My App) overwrites server tag Data resolves title = Product Name correct — but late t0 · server t1 · hydrate t2 · resolve Crawler snapshot in this window indexes the default title
Between the server's correct title and the resolved one, hydration flashes a default — and a crawler that snapshots the DOM in that window indexes the wrong title.

Validation

  • document.title after load equals the server-rendered title, with no flash.
  • GSC URL Inspection rendered HTML shows the correct title and description.
  • No console hydration warning for the metadata components.
  • Throttled CPU reload shows no visible default-then-correct title flash.

Each of those checks passes for the same underlying reason: the client’s first render and the server’s render read the same data, so hydration finds equal values and changes nothing.

Matching server and client metadata makes hydration a no-op When the server-rendered title and the client's first-render title both derive from the same route data and match, hydration preserves the tag instead of overwriting it. Same data on both sides preserves the tag Route data serialized once Server-rendered title Product Name Client first render Product Name values equal? yes no-op tag preserved
When both sides compute the title from the same data, hydration finds equal values and leaves the server's tag in place.

Reference

// Pass server data to the client so first render matches — no overwrite
export function Head({ meta }) {
  // meta is serialized from the server render and available on first client render
  return (
    <>
      <title>{meta.title}</title>
      <meta name="description" content={meta.description} />
      <link rel="canonical" href={meta.canonical} />
    </>
  );
}

The choice comes down to where the head’s first value comes from: a client-side default that overwrites the server, or the server’s own data that reconciles cleanly.

Two ways to set the head during hydration Mounting with a default value overwrites the server tag and flashes a placeholder, while deriving the head from server data keeps the correct title with no flash. Two ways to set the head — one overwrites, one preserves Mount with a default · first render sets a placeholder · effect overwrites after fetch · server tag lost, title flashes · crawler may index the default Derive from server data · first render uses the real data · head equals the server value · hydration is a no-op, no flash · crawler sees the right title
The overwrite disappears when the client's first render derives the head from the same data the server used.

Frequently Asked Questions

Why do my meta tags reset to defaults after the page loads? The client re-renders during hydration with default metadata before the route’s real data is available, overwriting the correct server-rendered tags. The fix is to compute the same metadata on the client from the same data the server used, so the first client render matches.

Does a brief meta flash hurt SEO? If a crawler snapshots the DOM during the default-value window, it can index the wrong title or description. Even when it does not, the flash signals that client metadata is not aligned with the server, which is a fragile state worth fixing.

← Back to Avoiding Metadata Hydration Pitfalls