//
Search across all documentation pages
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
function BlogPost({ post }: { post: { title: string; description: string; author: string } }) {
return (
<article>
{/* These are hoisted to <head> automatically */}
<title>{post.title}</title>
<meta name="description" content={post.description} />
<meta name="author" content={post.author} />
<link rel="canonical" href={`https://example.com/blog/${post.title}`} />
<h1>{post.title}</h1>
<p>{post.description}</p>
</article>
);
}When to reach for this: Use built-in metadata whenever a component needs to set the page title, description, or other head tags. This eliminates the need for react-helmet or manual document.title effects.
// A multi-page app where each route sets its own metadata
function ProductPage({ product }: {
product: {
name: string;
description: string;
price: number;
image: string;
category: string;
};
}) {
return (
<main>
{/* Page title */}
<title>{product.name} | My Store</title>
{/* SEO meta tags */}
<meta name="description" content={product.description} />
<meta name="keywords" content={`${product.category}, buy, shop`} />
{/* Open Graph tags */}
<meta property="og:title" content={product.name} />
<meta property="og:description" content={product.description} />
<meta property="og:image" content={product.image} />
<meta property="og:type" content="product" />
{/* Twitter card */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={product.name} />
<meta name="twitter:image" content={product.image} />
{/* Preload the product image */}
<link rel="preload" href={product.image} as="image" />
{/* Stylesheet for this page */}
<link rel="stylesheet" href="/styles/product.css" precedence="default" />
{/* Page content */}
<div className="max-w-4xl mx-auto p-6">
<img src={product.image} alt={product.name} />
<h1 className="text-3xl font-bold">{product.name}</h1>
<p className="text-xl">${product.price.toFixed(2)}</p>
<p>{product.description}</p>
</div>
</main>
);
}
function SettingsPage() {
return (
<div>
<title>Settings | My Store</title>
<meta name="robots" content="noindex" />
<h1>Account Settings</h1>
{/* ... */}
</div>
);
}
export { ProductPage, SettingsPage };What this demonstrates:
<title> rendered inline, automatically hoisted to <head><link rel="preload"> for performance optimization<link rel="stylesheet"> with precedence for CSS ordering<title>, <meta>, and <link> elements when rendered inside a component and hoists them to the document <head>.<head>. During client-side navigation, React updates the <head> by adding, removing, or updating the hoisted elements.<title> tags are deduplicated -- only the last rendered <title> wins if multiple components render one.<meta> tags are deduplicated by the name or property attribute. Two <meta name="description"> tags from different components will result in only the most recent one.<link rel="stylesheet"> tags support a precedence attribute that controls insertion order. Stylesheets with the same precedence are grouped together. React also deduplicates stylesheet links by href.<link> types (preload, icon, canonical, etc.) are hoisted but not deduplicated by default.Dynamic title with live data:
function ChatRoom({ roomName, unreadCount }: { roomName: string; unreadCount: number }) {
const titlePrefix = unreadCount > 0 ? `(${unreadCount}) ` : "";
return (
<div>
<title>{titlePrefix}{roomName} | Chat</title>
<h1>{roomName}</h1>
{/* chat UI */}
</div>
);
}Nested components adding metadata:
function Layout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
{/* Base metadata */}
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
{children}
</body>
</html>
);
}
function AboutPage() {
return (
<Layout>
{/* Page-specific metadata merges with layout metadata */}
<title>About Us</title>
<meta name="description" content="Learn about our company" />
<h1>About Us</h1>
</Layout>
);
}Stylesheet precedence ordering:
function App() {
return (
<>
{/* "default" precedence loads first */}
<link rel="stylesheet" href="/base.css" precedence="default" />
{/* "high" precedence loads after "default" */}
<link rel="stylesheet" href="/theme.css" precedence="high" />
</>
);
}
function Widget() {
return (
<>
{/* Same precedence groups together with other "default" stylesheets */}
<link rel="stylesheet" href="/widget.css" precedence="default" />
<div>Widget content</div>
</>
);
}<title>, <meta>, and <link> JSX elements use their standard HTML attribute types from React.JSX.IntrinsicElements.precedence attribute on <link> is a React-specific extension typed as string | undefined.<title> tags -- Only the last rendered <title> applies. If two sibling components both render <title>, the result depends on render order. Fix: Set <title> in only one component per route, typically the page-level component.metadata export and generateMetadata. Using both that and inline <title> tags can cause duplicates. Fix: Choose one approach per project. For Next.js apps, prefer the framework's metadata API.<head> in SSR HTML -- React hoists tags during rendering, but if you inspect the raw HTML response, they appear in <head>. If hydration mismatches occur, check for server/client differences. Fix: Ensure metadata values are deterministic (no Date.now() or Math.random()).<link rel="stylesheet"> without precedence -- If you omit precedence, the stylesheet link is treated as a regular link and is not deduplicated or ordered by React. Fix: Always add precedence to stylesheet links for React-managed ordering.<script> hoisting -- React 19 does not hoist <script> tags to <head>. Fix: Use preinit from react-dom for scripts, or place script tags in your HTML template.
| Approach | When to choose |
|---|---|
| Built-in metadata (React 19) | Default choice for new React 19 projects |
Next.js metadata export | Next.js App Router projects with static or dynamic metadata |
react-helmet / react-helmet-async | React 18 or older projects |
Manual document.title in useEffect | Quick hack for client-only title changes |
HTML <head> in template | Static metadata that never changes |
<head> automatically<head> by adding, removing, or updating hoisted elements<title> applies -- React deduplicates them<title>, the result depends on render order<title> in only one component per route, typically the page-level component<meta> tags are deduplicated by the name or property attribute<meta name="description"> tags from different components result in only the most recent one<link> types (preload, icon, canonical) are hoisted but not deduplicated by defaultprecedence controls insertion order of stylesheets in the <head>precedence value are grouped togetherhref and suspends rendering until the stylesheet loadsfunction Page({ title, description, image }) {
return (
<>
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={image} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
{/* page content */}
</>
);
}function ChatRoom({ roomName, unreadCount }) {
const prefix = unreadCount > 0 ? `(${unreadCount}) ` : "";
return (
<>
<title>{prefix}{roomName} | Chat</title>
{/* chat UI */}
</>
);
}The title updates reactively as unreadCount changes.
<meta charSet>, <link rel="icon">) and child pages add page-specific tags<title> overrides the layout's if both render one<meta> deduplication by name/property ensures no duplicatesmetadata export and generateMetadata which may conflict with inline tags<head>metadata API<script> tags to <head>preinit from react-dom for scripts, or place script tags in your HTML template<title>, <meta>, and <link> elementsprecedence, the stylesheet is treated as a regular link and is not deduplicated or ordered by Reactprecedence to stylesheet links you want React to manageprecedence attribute on <link> is a React-specific extension typed as string | undefined<title>, <meta>, and <link> use their standard HTML attribute types from React.JSX.IntrinsicElementsDate.now(), Math.random(), or other non-deterministic valuespreload and preinitReviewed by Chris St. John·Last updated Jul 19, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥