<meta>

Attaches document metadata such as description, keywords or Open Graph tags, hoisted into the head.

Signature
<meta name="keywords" content="React, JavaScript, semantic markup, html" />
  • Added in React 19.0.0

Parameters

ParameterTypeDescription
$nameoptionalstringThe kind of metadata to attach to the document, e.g. "description" or "viewport".
$charsetoptionalstringThe character set used by the document. The only valid value is "utf-8". React also accepts the DOM property spelling charSet.
$httpEquivoptionalstringA directive for processing the document.
$itemPropoptionalstringMetadata about a particular item within the document rather than the document as a whole. Its presence disables hoisting.
$contentoptionalstringThe 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

Example · jsx
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>.

Example · jsx
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.

Example · jsx
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

  • Be the first to comment on this lesson.