Thread-safe Helmet for React 16+ and friends
npm install react-v19-helmet-asyncForked from staylor/react-helmet-async under the Apache-2.0 license by Yassine Boumiza in 2025. Extended compatibility to React v18+ including React v19.0.0 and v19.1.0.
Repository: https://github.com/advisely/react-v19-helmet-async
Announcement post on Times Open blog
This package is a fork of React Helmet. usage is synonymous, but server and client now requires to encapsulate state per request.
react-helmet relies on react-side-effect, which is not thread-safe. If you are doing anything asynchronous on the server, you need Helmet to encapsulate data on a per-request basis, this package does just that.
| Metric | % |
|------------|--------|
| Statements | 97.75% |
| Branches | 96.30% |
| Functions | 97.01% |
| Lines | 97.75% |
New is 1.0.0: No more default export! import { Helmet } from 'react-helmet-async'
The main way that this package differs from react-helmet is that it requires using a Provider to encapsulate Helmet state for your React tree. If you use libraries like Redux or Apollo, you are already familiar with this paradigm:
``javascript
import React from 'react';
import ReactDOM from 'react-dom';
import { Helmet, HelmetProvider } from 'react-helmet-async';
const app = (
Hello World
);
ReactDOM.hydrate(
app,
document.getElementById(โappโ)
);
`
On the server, we will no longer use static methods to extract state. react-side-effect.rewind()
exposed a method, which Helmet used when calling Helmet.renderStatic(). Instead, we are goingcontext
to pass a prop to HelmetProvider, which will hold our state specific to each request.
`javascript
import React from 'react';
import { renderToString } from 'react-dom/server';
import { Helmet, HelmetProvider } from 'react-helmet-async';
const helmetContext = {};
const app = (
Hello World
);
const html = renderToString(app);
const { helmet } = helmetContext;
// helmet.title.toString() etcโฆ
`
This package only works with streaming if your
data is output outside of renderToNodeStream().
This is possible if your data hydration method already parses your React tree. Example:`javascript
import through from 'through';
import { renderToNodeStream } from 'react-dom/server';
import { getDataFromTree } from 'react-apollo';
import { Helmet, HelmetProvider } from 'react-helmet-async';
import template from 'server/template';const helmetContext = {};
const app = (
Hello World
Hello World
);
await getDataFromTree(app);
const [header, footer] = template({
helmet: helmetContext.helmet,
});
res.status(200);
res.write(header);
renderToNodeStream(app)
.pipe(
through(
function write(data) {
this.queue(data);
},
function end() {
this.queue(footer);
this.queue(null);
}
)
)
.pipe(res);
`Usage in Jest ๐งช
While testing in using jest, if there is a need to emulate SSR, the following string is required to have the test behave the way they are expected to.`javascript
import { HelmetProvider } from 'react-helmet-async';HelmetProvider.canUseDOM = false;
`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 component allows the server render of react-helmet-async to expose a method for prioritizing relevant SEO tags.In the component:
`javascript
A fancy webpage
`In your server template:
`javascript
${helmet.title.toString()}
${helmet.priority.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
${helmet.script.toString()}
...
`Will result in:
`html
A fancy webpage
...
`A list of prioritized tags and attributes can be found in constants.ts.
Usage without Context ๐งฉ
You can optionally use outside a context by manually creating a stateful HelmetData instance, and passing that stateful object to each instance:
`js
import React from 'react';
import { renderToString } from 'react-dom/server';
import { Helmet, HelmetProvider, HelmetData } from 'react-helmet-async';const helmetData = new HelmetData({});
const app = (
Hello World
Hello World
);
const html = renderToString(app);
const { helmet } = helmetData.context;
``Licensed under the Apache 2.0 License, Copyright 2018 Scott Taylor