Updating Document Title in React Without Helmet
react-helmet has long been the default for managing the head in React, but it is an extra dependency with its own quirks — and modern React often does not need it. For client-rendered routes you can manage the title with a small hook over the native document API, and on React 19 you can render <title> and <meta> directly in JSX and let React hoist them. This keeps titles correct per route as part of programmatic title and meta tag updates.
Step-by-step fix
-
Write a minimal title hook. Set
document.titlein an effect keyed to the route’s title.// useDocumentTitle.js import { useEffect } from 'react'; export function useDocumentTitle(title) { useEffect(() => { const prev = document.title; document.title = title; return () => { document.title = prev; }; // restore on unmount }, [title]); } -
Manage meta tags by mutation, not appending. Reuse one tag per name so navigation does not pile up duplicates.
// ✅ Update an existing meta tag in place function setMeta(name, content) { let tag = document.querySelector(`meta[name="${name}"]`); if (!tag) { tag = document.createElement('meta'); tag.name = name; document.head.appendChild(tag); } tag.content = content; } -
Prefer native metadata hoisting on React 19. Rendering metadata in JSX removes the need for any library and works during SSR.
// ✅ React 19 hoists these into <head> automatically function ProductHead({ product }) { return ( <> <title>{product.name}</title> <meta name="description" content={product.summary} /> </> ); } -
Set the title as early as possible for client-rendered routes so the render snapshot captures the correct value.
Validation
document.titlematches the route on every navigation.- No duplicate meta tags accumulate as you navigate.
- GSC URL Inspection rendered HTML shows the right title and description.
- React 19 path:
<title>appears in<head>in the SSR output, not buried in the body.
Reference
// Dependency-free head management for a route component
import { useDocumentTitle } from './useDocumentTitle';
export function ProductPage({ product }) {
useDocumentTitle(`${product.name} — Example`); // SEO: per-route title
useEffect(() => {
setMeta('description', product.summary); // mutate, never append
}, [product]);
return <ProductView product={product} />;
}
Frequently Asked Questions
Do I still need react-helmet in modern React? Not necessarily. React 19 hoists title and meta elements rendered anywhere in the tree into the document head, covering most needs natively. For earlier versions, a small custom hook that sets document.title and mutates meta tags is enough for many apps without the extra dependency.
Will a client-only title update be seen by Google? Google can pick up a client-set title after it renders the page, but it is more reliable when the title is in the server or prerendered HTML. For client-rendered routes, set the title as early as possible and ensure it is correct in the render snapshot.
Related
- Programmatic Title & Meta Tag Updates — the broader head-management guide.
- How to Update Meta Tags on Route Change in React — the route-change pattern in depth.
- Fixing meta tags overwritten during hydration — keep these updates from being wiped.
← Back to Programmatic Title & Meta Tag Updates