<meta>
Attaches document metadata such as description, keywords or Open Graph tags, hoisted into the head.
<meta name="keywords" content="React, JavaScript, semantic markup, html" />- Added in React 19.0.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$nameoptional | string | The kind of metadata to attach to the document, e.g. "description" or "viewport". |
$charsetoptional | string | The character set used by the document. The only valid value is "utf-8". React also accepts the DOM property spelling charSet. |
$httpEquivoptional | string | A directive for processing the document. |
$itemPropoptional | string | Metadata about a particular item within the document rather than the document as a whole. Its presence disables hoisting. |
$contentoptional | string | The metadata attached when used with name or itemProp, or the behaviour of the directive when used with httpEquiv. |
Return value
Renders nothing visible. React places the <meta> DOM node in the document <head>, unless it carries an itemProp.
Changelog
- PHP 19.0.0<meta> rendered anywhere in the React tree is hoisted into the document <head>. Before 19 it rendered in place and needed a helper library such as react-helmet.
<meta> describes the document to browsers, crawlers and social platforms. Since React 19 you can render it from any component and React moves the node into <head>, which is the only place it is valid. The point is composability: the component that knows the page — an article, a product, a search result — is the component that writes the description and the Open Graph tags for it.
Exactly one of four props
A <meta> must carry exactly one of name, httpEquiv, charset or itemProp; each selects a different mode, and content supplies the value. name plus content covers almost everything you write day to day, including og: and twitter: tags.
What trips people up
An itemProp switches off hoisting entirely. That is deliberate — the tag then annotates one part of the page for structured-data crawlers, so it must stay next to the element it describes inside an itemScope. React also does not de-duplicate metadata: render two components that each declare name="description" and both land in the head, leaving crawlers to guess. And Open Graph uses property, not name, in the specification; React passes property through unchanged, so write whichever the consumer expects.
Example
function SocialTags({ title, description, image, url }) {
return (
<>
<meta name="description" content={description} />
<meta property="og:type" content="article" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={image} />
<meta property="og:url" content={url} />
<meta name="twitter:card" content="summary_large_image" />
</>
);
}
export default function ArticlePage() {
const article = {
title: "Hoisting document metadata",
description: "How React 19 moves meta, title and link tags into the head.",
image: "https://example.com/og/hoisting.png",
url: "https://example.com/blog/hoisting",
published: "2024-12-05"
};
return (
<article>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<SocialTags
title={article.title}
description={article.description}
image={article.image}
url={article.url}
/>
<h1>{article.title}</h1>
<p>Published {article.published}</p>
<p>{article.description}</p>
</article>
);
}When to use it
- Emitting per-page Open Graph and Twitter card tags from the route component that has the data
- Setting the description a search engine shows under the result for this page
- Declaring a robots directive such as noindex on a private or paginated view
- Annotating a product block with itemProp values for structured-data crawlers
Frequently asked questions
Do I still need react-helmet in React 19?
No, not for plain metadata. React 19 hoists <meta>, <title> and <link> into the document head from anywhere in the tree, which is what most helmet usage was for. A helmet-style library still earns its place if you rely on its de-duplication and override semantics, since React itself does not merge or replace competing tags.
Why do I have two description meta tags in the head?
React does not de-duplicate metadata, so a layout and a page component that each render name="description" both end up in <head>. Which one a crawler honours is undefined. Give each page exactly one owner for its metadata — usually the route component — and have the layout render only tags no page will restate, such as charset and viewport.
How do I set Open Graph tags per page in React?
Render <meta property="og:title" content={...} /> directly from the route component that has the data, and React hoists it into the head. Note that Open Graph uses property rather than name; React passes both through unchanged. Crawlers that do not execute JavaScript will only see these tags if the page is server-rendered or prerendered.
More examples
itemProp stays in place instead of hoisting
Inspecting the DOM shows the description tag in <head> and the two itemProp tags still inside the <section>. That split is the whole rule: metadata about the document is hoisted, metadata about a fragment of the page is not, because moving it would detach it from the itemScope that gives it meaning. The same exception applies to <link itemProp> and <title itemProp>.
function Product({ product }) {
return (
<section itemScope itemType="https://schema.org/Product">
{/* Hoisted: describes the whole document. */}
<meta name="description" content={`Buy the ${product.name}`} />
{/* Not hoisted: describes this section, so it renders right here. */}
<meta itemProp="sku" content={product.sku} />
<meta itemProp="priceCurrency" content="EUR" />
<h2 itemProp="name">{product.name}</h2>
<span itemProp="price">{product.price}</span>
</section>
);
}
export default function ProductPage() {
const product = { name: "Desk lamp", sku: "DL-114", price: "49.00" };
return <Product product={product} />;
}A robots directive that depends on state
With results on page one the noindex tag is absent from <head>; on an empty or paginated result set React mounts it and the tag appears. Unlike <style> and <script>, meta tags are ordinary elements as far as updates go — mounting and unmounting them adds and removes them from the head as you would expect. Note httpEquiv as the camelCase spelling of the http-equiv attribute.
function SearchResults({ query, results, page }) {
const shouldIndex = results.length > 0 && page === 1;
return (
<main>
{/* Rendering conditionally adds or removes the tag in <head>. */}
{!shouldIndex && <meta name="robots" content="noindex, follow" />}
<meta name="description" content={`Results for "${query}"`} />
<meta httpEquiv="content-language" content="en" />
<h1>{results.length} results for "{query}"</h1>
<ul>
{results.map((r) => (
<li key={r.id}>{r.title}</li>
))}
</ul>
</main>
);
}
export default function SearchPage() {
const results = [
{ id: "a", title: "Hoisting document metadata" },
{ id: "b", title: "Stylesheet precedence explained" }
];
return <SearchResults query="hoisting" results={results} page={1} />;
}
Discussion