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
-
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"? -
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; } -
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.
-
Mutate, don’t replace, the tags so hydration reconciles cleanly instead of tearing down and rebuilding the head.
Validation
document.titleafter 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.
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.
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.
Related
- Avoiding Metadata Hydration Pitfalls — the full hydration-metadata guide.
- Reducing layout shift during React hydration — the visual counterpart of hydration mismatch.
- Programmatic Title & Meta Tag Updates — managing the head across navigation.
← Back to Avoiding Metadata Hydration Pitfalls