# React Helmet

[![Latest NPM Release](https://img.shields.io/npm/v/@dr.pogodin/react-helmet.svg)](https://www.npmjs.com/package/@dr.pogodin/react-helmet)
[![NPM Downloads](https://img.shields.io/npm/dm/@dr.pogodin/react-helmet.svg)](https://www.npmjs.com/package/@dr.pogodin/react-helmet)
[![CircleCI](https://dl.circleci.com/status-badge/img/gh/birdofpreyru/react-helmet/tree/master.svg?style=shield)](https://app.circleci.com/pipelines/github/birdofpreyru/react-helmet)
[![GitHub repo stars](https://img.shields.io/github/stars/birdofpreyru/react-helmet?style=social)](https://github.com/birdofpreyru/react-helmet)
[![Dr. Pogodin Studio](https://raw.githubusercontent.com/birdofpreyru/react-helmet/master/.README/logo-dr-pogodin-studio.svg)](https://dr.pogodin.studio/docs/react-helmet)

[react-helmet-async]: https://github.com/staylor/react-helmet-async

Advanced management of document head's elements (`<base>`, `<link>`, `<meta>`,
`<noscript>`, `<script>`, `<style>`, `<title>`), and of attributes of `<body>`
and `<html>` elements in React 19+ applications. This library is a proud
successor of now unmaintained and stale
[react-helmet-async]<sup>([note][`@dr.pogodin/react-helmet` _vs_ `react-helmet-async`])</sup> and
[react-helmet](https://github.com/nfl/react-helmet) libraries.

[![Sponsor](https://raw.githubusercontent.com/birdofpreyru/js-utils/master/.README/sponsor.svg)](https://github.com/sponsors/birdofpreyru)

### Sponsored By
[<img width=36 src="https://avatars.githubusercontent.com/u/17030877?v=4&s=36" />](https://github.com/RigoOnRails)

### [Contributors](https://github.com/birdofpreyru/react-helmet/graphs/contributors)
[<img width=36 src="https://avatars.githubusercontent.com/u/5297725?v=4&s=36" />](https://github.com/vegerot)
[<img width=36 src="https://avatars.githubusercontent.com/u/20144632?s=36" />](https://github.com/birdofpreyru)

## Table of Contents
- [Getting Started]
  - [`@dr.pogodin/react-helmet` _vs_ pure React]
  - [`@dr.pogodin/react-helmet` _vs_ `react-helmet-async`]
- [Prioritizing Tags for SEO]
- [Reference]
  - [Helmet] &mdash; specifies elements and attributes to be created / set /
    overriden.
  - [HelmetServerState] &mdash; State object for server-side rendering (SSR)
    purposes.
  - [HelmetProvider] &mdash; provides [React Context] to [Helmet] components.
  - [MetaTags] &mdash; helper component for easier rendering of page title,
    description, and meta-data for search engines, and social graphs (_i.e._
    previews of linked pages in social networks, messengers, _etc._).

## Getting Started
[Getting Started]: #getting-started

To install the library:
```sh
npm install --save @dr.pogodin/react-helmet
```

At a high level, wrap the main application tree into [HelmetProvider]:
```tsx
import type { FunctionComponent } from 'react';
import { HelmetProvider } from '@dr.pogodin/react-helmet';

const YourApp: FunctionComponent = () => {
  /* Whatever code you need. */
  return (
    <HelmetProvider>
      { /* Your application tree. */ }
    </HelmetProvider>
  );
}
```

Anywhere within the [HelmetProvider]'s children tree use [Helmet] component
to set / modify document head's elements, or supported attributes of `<body>`
and `<html>` elements. Instances of [Helmet] component within the application
tree add or override elements and attributes in the order these [Helmet]
instances are rendered.

<a id="example"></a>

[Example]: #example

```tsx
import type { FunctionComponent } from 'react';
import { Helmet } from '@dr.pogodin/react-helmet';

const SomeComponent: FunctionComponent = () => {
  /* Whatever code you need. */
  return (
    <div>
      <Helmet>
        <title>My Title</title>
        <link rel="canonical" href="http://mysite.com/example" />
        <meta charSet="utf-8" />
        <meta name="description" content="Some Component" />
      </Helmet>
      { /* Whatever other stuff you need. */ }

      { /* For example, this other <Helmet> component will override the title
           and description set earlier in the render tree. */ }
      <Helmet>
        <title>Overriden Title</title>
        <meta name="description" content="Overriden Component Description" />
      </Helmet>
    </div>
  )
};
```

Alternatively, all elements and attributes specified by [Helmet] components may
be provided _via_ props instead of children.
```tsx
import type { FunctionComponent } from 'react';
import { Helmet } from '@dr.pogodin/react-helmet';

const SomeComponent: FunctionComponent = () => {
  /* Whatever code you need. */
  return (
    <div>
      <Helmet
        title="My Title"
        link={[{
          href: 'http://mysite.com/example',
          rel: 'canonical',
        }]}
        meta={[{
          charSet: 'utf-8',
        }, {
          content: 'Some Component',
          name: 'description',
        }]}
      />
      { /* Whatever other stuff you need. */ }

      { /* For example, this other <Helmet> component will override the title
           and description set earlier in the render tree. It is also fine to
           use a mix of props and children. */ }
      <Helmet title="Overriden Title">
        <meta name="description" content="Overriden Component Description" />
      </Helmet>
    </div>
  )
};
```

For the server-side rendering (SSR) purposes you pass in a `onServerState()`
callback to the [HelmetProvider], and use the argument received by this callback
to retrieve the string, or component representation of the elements and attributes
to be injected into the document head (if you use streaming for server side
rendering, you should output your `<head>` data outside `renderToNodeStream()`):
```tsx
import { type HelmetServerState, HelmetProvider } from '@dr.pogodin/react-helmet';

async function yourServerSideRenderingFunction() {
  // ...
  let state: HelmetServerState | undefined;
  const { prelude } = await prerenderToNodeStream(
    <HelmetProvider
      onServerState={(s) => {
        state = s;
      }}
    >
      { /* Your application tree. */ }
    </HelmetProvider>
  );
  // ...

  if (!state) throw Error('SSR failed');

  // For example, this is how you get the string representation of <meta> tags
  // to be injected into your document head.
  const metaElements = state.meta?.toString();
}
```

### `@dr.pogodin/react-helmet` _vs_ pure React
[`@dr.pogodin/react-helmet` _vs_ pure React]: #drpogodinreact-helmet-vs-pure-react

There is some confusion regarding the need for React Helmet since React v19
release, which
[natively supports document meta tags](https://react.dev/blog/2024/12/05/react-19#support-for-metadata-tags)
 like `<title>`, `<link>`,
and `<meta>`; although the official React documentation is crystal clear in this
regard, [noting](https://react.dev/blog/2024/12/05/react-19#you-may-still-want-a-metadata-library):

> _**You may still want a Metadata library**_
>
> _For simple use cases, rendering Document Metadata as tags may be suitable,
> but libraries can offer more powerful features like **overriding generic metadata
> with specific metadata based on the current route**. These features make it easier
> for frameworks and libraries like react-helmet to support metadata tags, rather
> than replace them._

then React's documentation for `<title>`
[warns further](https://react.dev/reference/react-dom/components/title#special-rendering-behavior):

> _**Pitfall**_
>
> _Only render a single `<title>` at a time. If more than one component renders
> a `<title>` tag at the same time, React will place all of those titles in
> the document head. When this happens, the behavior of browsers and search
> engines is undefined._

In other words, React only hoists up to the document head all `<title>`,
`<link>`, `<meta>`, and other meta tags it encounters up to the document head,
but it does not dedupe them, as this React Helmet library does.

Thus, while the [Example] given above (this library) renders to your document
head the following:
```html
<title>Overriden Title</title>
<link href="http://mysite.com/example" rel="canonical" data-rh="true">
<meta charset="utf-8" data-rh="true">
<meta content="Overriden Component Description" name="description" data-rh="true">
```

the equivalent code relying on pure React, without a meta-data library will
render:
```html
<title>Overriden Title</title>
<title>My Title</title>
<link href="http://mysite.com/example" rel="canonical">
<meta charset="utf-8">
<meta content="Some Component" name="description">
<meta content="Overriden Component Description" name="description">
```

Such HTML is just against the specifications which tells,
[for example](https://html.spec.whatwg.org/multipage/semantics.html#the-title-element):

> _There **must be no more than one** `title` element per document._

and you are at the mercy of different browsers, protocols, and web crawlers
handling such code in their own ways.

### `@dr.pogodin/react-helmet` _vs_ `react-helmet-async`
[`@dr.pogodin/react-helmet` _vs_ `react-helmet-async`]: #drpogodinreact-helmet-vs-react-helmet-async

You may have noticed that the claim that [react-helmet-async] is _unmaintained
and stale_ does not hold now, after its sudden v3.0.0 release on March 3, 2026.
The following timeline explains the situation best:

- About **December 2023** the latest pre-v3 version of [react-helmet-async] (v2.0.5)
  was released (the latest React version was v18.2.0 back then).

- **December 2024** React v19.0.0 was released, which broke the latest
  [react-helmet-async] v2.0.5. Corresponding (duplicated) issue tickets started
  to pop-up in the [react-helmet-async] repo right away:
  [#237](https://github.com/staylor/react-helmet-async/issues/237),
  [#238](https://github.com/staylor/react-helmet-async/issues/238),
  [#239](https://github.com/staylor/react-helmet-async/issues/239),
  [#244](https://github.com/staylor/react-helmet-async/issues/244),
  [#249](https://github.com/staylor/react-helmet-async/issues/249).
  There was no reaction from the repository owner / maintainer, nor any other
  signs of activity in the project.

- **February 2025**, after waiting for a few months, and seeing no reaction from
  the project owner, I decided to take the matter into my own hands, by forking,
  fixing, and further maintaining the library; which I
  [announced right away](https://github.com/staylor/react-helmet-async/issues/244#issuecomment-2676342557),
  to save other people from duplicating the efforts on the same open source work,
  which generates no direct revenue to anybody.

- **April 2025**, I posted additional announcement in the [react-helmet-async]
  repo [#254](https://github.com/staylor/react-helmet-async/issues/254) that
  the project has been taken over.

- **March 2026**, out of the blue the owner of [react-helmet-async] publishes
  a new, upgraded version v3.0.0, and closes all aforementioned tickets without
  any comments.

In the view of this development, here are several reasons why you should still
stick (or migrate) to this `@dr.pogodin/react-helmet` library, and stay away
from [react-helmet-async]:

- The owner of [react-helmet-async] has actually fallen into the pitfal of
  _React v19 native support for meta tags_, explained
  [above][`@dr.pogodin/react-helmet` _vs_ pure React],
  and broke his library &mdash; when running within React v19 environment it now
  &laquo;_renders actual DOM elements and lets React handle hoisting them to
  `<head>`_&raquo;. As the result (as of its v3.0.0) it now fails to dedupe
  meta tags as it was supposed to do (_e.g._ it will render multiple `<title>`
  tags into `<head>`, similarly to the pure React); and drops support of some
  features (_e.g._ [Prioritizing Tags for SEO]).

- This (`@dr.pogodin/react-helmet`) library has some new features
  (_e.g._ [MetaTags] component) absent in [react-helmet-async].

- This (`@dr.pogodin/react-helmet`) library is properly, and timely maintained,
  and it will be maintained so in the foreseeable future.

## Prioritizing Tags for SEO
[Prioritizing Tags for SEO]: #prioritizing-tags-for-seo

It is understood that in some cases for SEO, certain tags should appear earlier
in the HEAD. Using the `prioritizeSeoTags` flag on any `<Helmet>` component
allows the server render of @dr.pogodin/react-helmet to expose a method for
prioritizing relevant SEO tags.

In the component:
```tsx
<Helmet prioritizeSeoTags>
  <title>A fancy webpage</title>
  <link rel="notImportant" href="https://www.chipotle.com" />
  <meta name="whatever" value="notImportant" />
  <link rel="canonical" href="https://www.tacobell.com" />
  <meta property="og:title" content="A very important title"/>
</Helmet>
```

In your server template:
```tsx
<html>
  <head>
    ${helmet.title.toString()}
    ${helmet.priority.toString()}
    ${helmet.meta.toString()}
    ${helmet.link.toString()}
    ${helmet.script.toString()}
  </head>
  ...
</html>
```

Will result in:
```html
<html>
  <head>
    <title>A fancy webpage</title>
    <meta property="og:title" content="A very important title"/>
    <link rel="canonical" href="https://www.tacobell.com" />
    <meta name="whatever" value="notImportant" />
    <link rel="notImportant" href="https://www.chipotle.com" />
  </head>
  ...
</html>
```

A list of prioritized tags and attributes (`SEO_PRIORITY_TAGS`) can be found in
[constants.ts](./src/constants.ts).

## Reference
[Reference]: #reference

### Helmet
[Helmet]: #helmet

The [Helmet] component specifies (document head) elements and attributes to be
created / set / overriden by the library. Different [Helmet] instances within
the application tree (within the single [HelmetProvider]) have effect in the
order they are encountered (mounted to DOM) during the render.

The [Helmet] component exposes two equivalent APIs &mdash; the elements /
attributes may be provided either as component's children (_i.e._ written as
regular JSX component children), or as [Helmet] component props. Both these
APIs can also be used at the same time, with values provided as props handled
as additional JSX children, appearing prior the explicitly provided JSX children.

**Props**
- `base` &mdash; _To be documented_

- `bodyAttributes` &mdash; _To be documented_

- `defaultTitle` &mdash; **string** | **undefined** &mdash; Optional.
  The fallback to use when `titleTemplate` (below) prop is provided,
  but no title was specified:
  ```tsx
  // JSX code:
  <Helmet
    defaultTitle="My Site"
    titleTemplate"My Site - %s"
  />

  // DOM output:
  <head>
    <title>My Site</title>
  </head>
  ```

- `defer` &mdash; **boolean** | **undefined** &mdash; Optional. Defaults _true_.
  set to _false_ to not use `requestAnimationFrame` and instead update the DOM
  as soon as possible. Useful if you want to update the title when the tab is
  out of focus.

- `encodeSpecialCharacters` &mdash; **boolean** | **undefined** &mdash;
  Optional. Defaults _true_. Set _false_ to disable string encoding by
  `.toString()` methods of [HelmetServerState].

- `htmlAttributes` &mdash; _To be documented_

- `link` &mdash; _To be documented_

- `meta` &mdash; _To be documented_

- `noscript` &mdash; _To be documented_

- `onChangeClientState` &mdash; **function** | **undefined** &mdash; Optional.
  A callback to trigger on client-side each time the head elements / attributes
  are updated. It will be called with three arguments:
  - `newState` &mdash; _To be documented_
  - `addedTags` &mdash; _To be documented_
  - `removedTags` &mdash; _To be documented_

- `prioritizeSeoTags` &mdash; _To be documented_

- `script` &mdash; _To be documented_

- `style` &mdash; _To be documented_

- `title` &mdash; _To be documented_

- `titleAttributes` &mdash; _To be documented_

- `titleTemplate` &mdash; **string** | **undefined** &mdash; Optional.
  Allows to inherit title from a template, _e.g._
  ```tsx
  // JSX code:
  <Helmet titleTemplate="%s | MyAwesomeWebsite.com">
    <title>Nested Title</title>
  </Helmet>

  // DOM output:
  <head>
    <title>Nested Title | MyAwesomeWebsite.com</title>
  </head>
  ```

**Children**

```tsx
<Helmet>
    {/* html attributes */}
    <html lang="en" amp />

    {/* body attributes */}
    <body className="root" />

    {/* title attributes and value */}
    <title itemProp="name" lang="en">My Plain Title or {`dynamic`} title</title>

    {/* base element */}
    <base target="_blank" href="http://mysite.com/" />

    {/* multiple meta elements */}
    <meta name="description" content="Helmet application" />
    <meta property="og:type" content="article" />

    {/* multiple link elements */}
    <link rel="canonical" href="http://mysite.com/example" />
    <link rel="apple-touch-icon" href="http://mysite.com/img/apple-touch-icon-57x57.png" />
    <link rel="apple-touch-icon" sizes="72x72" href="http://mysite.com/img/apple-touch-icon-72x72.png" />
    {locales.map((locale) => {
        <link rel="alternate" href="http://example.com/{locale}" hrefLang={locale} key={locale}/>
    })}

    {/* multiple script elements */}
    <script src="http://include.com/pathtojs.js" type="text/javascript" />

    {/* inline script elements */}
    <script type="application/ld+json">{`
        {
            "@context": "http://schema.org"
        }
    `}</script>

    {/* noscript elements */}
    <noscript>{`
        <link rel="stylesheet" type="text/css" href="foo.css" />
    `}</noscript>

    {/* inline style elements */}
    <style type="text/css">{`
        body {
            background-color: blue;
        }

        p {
            font-size: 12px;
        }
    `}</style>
</Helmet>
```

### HelmetServerState
[HelmetServerState]: #helmetserverstate

The [HelmetServerState] object allows to get all elements and attributes
to be rendered accoding to all [Helmet] instances within [HelmetProvider] tree.
It is primarily intended for server-side rendering (SSR) purposes. It is defined
as:
```tsx
type HelmetServerState = {
  base: HelmetDatum;
  bodyAttributes: HelmetDatum;
  htmlAttributes: HelmetDatum;
  link: HelmetDatum;
  meta: HelmetDatum;
  noscript: HelmetDatum;
  script: HelmetDatum;
  style: HelmetDatum;
  title: HelmetDatum;
  titleAttributes?: HelmetDatum;
  priority: HelmetDatum;
};
```
Where each `HelmetDatum` has two methods allowing to get string and component
representation of the corresponding elements or attributes:
```tsx
type HelmetDatum = {
  toString(): string;
  toComponent(): ReactNode;
};
```

### HelmetProvider
[HelmetProvider]: #helmetprovider

The [HelmetProvider] component provides [React Context] to [Helmet]
components, _i.e._ any [Helmet] components in the application tree must be
descendants of a single [HelmetProvider] instance.

**Props**
- `children` &mdash; **ReactNode** &mdash; The component tree to render in
  the place of [HelmetProvider].
- `onServerState` &mdash; **(state: [HelmetServerState]) => void** &mdash;
  Optional. Callback to receive the state object for server-side rendering (SSR)
  purposes.

### MetaTags
[MetaTags]: #metatags
The [MetaTags] component is a helper for easier rendering of page title,
description, and misc meta tags for search engines and social graphs (_i.e._ for
previews of linked pages in social media, messengers, _etc._). Based on provided
properties it uses [Helmet] to render:
- `<title>` and `<meta name="description">` tags.
- Meta tags for [Open Graph].
- Meta tags for [Twitter (X) Cards].

```tsx
// Simple Example.

import type { FunctionComponent } from 'react';
import { MetaTags } from '@dr.pogodin/react-helmet';

const Example: FunctionComponent = () => (
  <MetaTags
    description="Example page description"
    title="Example page title"
  />
);

// It is completely equivalent to:

import type { FunctionComponent } from 'react';
import { Helmet } from '@dr.pogodin/react-helmet';

const EquivalentComponent: FunctionComponent = () => (
  <Helmet>
    <title>Example page title</title>
    <meta content="Example page description" name="description" />

    <meta content="summary_large_image" name="twitter:card" />
    <meta content="Example page title" name="twitter:title" />
    <meta content="Example page description" name="twitter:description" />

    <meta content="Example page title" property="og:title" />
    <meta content="Example page description" property="og:description" />
  </Helmet>
);
```
As this example demonstrates, [MetaTags] must not be wrapped into [Helmet],
as it will render its instance itself, but the same as [Helmet] components,
all [MetaTags] instances should be descendants of [HelmetProvider] within
the application component tree.

Multiple [MetaTags] within the application tree override the tags they manage
according to the usual [Helmet] logic.

**Required Properties**:

- `description` &mdash; **string** &mdash; Page description to use in
  the _description_ meta tag, and as the default description in [Open Graph]
  and [Twitter (X) Cards].

- `title` &mdash; **string** &mdash; The page name to use in the `<title>` tag,
  and as the default title in [Open Graph] and [Twitter (X) Cards].

**Optional Properties**:

- `children` &mdash; **ReactNode** &mdash; Component children, if any,
  are rendered at the component's place.

  All meta data injected by [MetaTags] instance are passed down the children
  tree using an auxiliary context, thus facilitating tags modification
  by children. For example:

  ```tsx
  import { type FunctionComponent, use } from 'react';
  import { MetaTags } from '@dr.pogodin/react-helmet';

  const Child: FunctionComponent = () => {
    // These are values injected by parent <MetaTags> component, if any.
    const { title, description, ...rest } = use(MetaTags.Context);

    // Say, you may modify the "parent" title like this:
    return <MetaTags title={`${title} / Child Component`} />;
  };

  const Parent: FunctionComponent = () => (
    <MetaTags title="Parent Component">
      <Child />
    </MetaTags>
  );
  ```

- `extraMetaTags` &mdash; **Array&lt;\{ content: string; name: string \}&gt;**
  &mdash; Allows to add additional, arbitrary `<meta>` tags to the page, with
  given `content` and `name` strings.

- `image` &mdash; **string** &mdash; The absolute URL of thumbnail image to use
  in [Open Graph] and [Twitter (X) Cards] meta tags (`twitter:image`,
  and `og:image`). By default these tags are not injected.

  **BEWARE:** The value must be a complete, absolute URL, including the correct
  website domain and HTTP schema.

- `siteName` &mdash; **string** &mdash; The site name to use in `twitter:site`,
  and `og:sitename` meta tags. By default these tags are not injected.

- `socialDescription` &mdash; **string** &mdash; The site description to use in
  `twitter:description` and `og:description` meta tags. By default the value of
  `description` prop is used.

- `socialTitle` &mdash; **string** &mdash; The page title to use in `twitter:title`,
  `og:title`, and `og:image:alt` meta tags. By default the value of `title` prop
  is used. Also the `og:image:alt` tag is only injected if `image` prop
  is present.

- `url` &mdash; **string** &mdash; The page URL to use in `og:url` meta tag.
  By default the tag is not injected.

[Open Graph]: https://ogp.me
[React Context]: https://react.dev/learn/passing-data-deeply-with-context
[Twitter (X) Cards]: https://developer.x.com/en/docs/x-for-websites/cards/overview/abouts-cards
