Thread-safe Async React Helmet
react-helmet-async is a robust solution for managing document head tags (title, meta, link, etc.) in React applications, designed to be thread-safe for server-side rendering (SSR). It addresses the limitations of the original `react-helmet` by encapsulating state on a per-request basis through a `<HelmetProvider>`, crucial for asynchronous SSR environments. The current stable version is 3.0.0, which introduces significant adaptations for React 19+, leveraging React's native metadata hoisting capabilities while maintaining backward compatibility for React 16-18. It differentiates itself by providing a consistent API across React versions, handling `htmlAttributes` and `bodyAttributes` consistently, and offering a context for SSR data extraction, although this context behaves differently in React 19. Releases appear to follow a non-strict cadence, driven by major React version updates and feature enhancements.
Common errors
-
TypeError: Cannot destructure property 'Helmet' of 'react-helmet-async' as it is undefined.
cause Attempting to use a default import for Helmet, but it was removed in v1.0.0.fixChange `import Helmet from 'react-helmet-async';` to `import { Helmet } from 'react-helmet-async';` -
Error: Invariant Violation: Hooks can only be called inside the body of a function component.
cause This error can occur if `react` is duplicated in your `node_modules` or if `react-helmet-async` is incorrectly linked, causing it to use a different React instance than your application. This often happens with monorepos or mismatched peer dependency resolutions.fixCheck for duplicate `react` installations using `npm ls react` or `yarn why react`. Use a tool like `resolutions` (Yarn) or `overrides` (NPM 8+) to force a single `react` version. Ensure `react` is correctly specified in `peerDependencies` and installed in your project. -
helmetContext.helmet is undefined (during SSR for React 19+)
cause When running on React 19+, `<title>`, `<meta>`, and `<link>` tags are handled natively by React's head hoisting. The `context` object passed to `HelmetProvider` will therefore not contain these specific tags.fixFor React 19+, rely on React's native head hoisting for `<title>`, `<meta>`, and `<link>`. If you need to access these tags programmatically on the server, render them directly in your component tree instead of relying solely on `Helmet` and its context extraction.
Warnings
- breaking Starting with version 1.0.0, the package no longer provides a default export. Attempting to use `import Helmet from 'react-helmet-async'` will result in a runtime error.
- breaking For React 19+, `<HelmetProvider>` becomes a transparent passthrough for `<title>`, `<meta>`, and `<link>` elements, as React 19 handles their hoisting natively. The `context` object for SSR will *not* be populated with these elements when running on React 19. `htmlAttributes` and `bodyAttributes` are still applied via direct DOM manipulation.
- gotcha Using `react-helmet-async` without `<HelmetProvider>` will lead to incorrect state management, especially in server-side rendering or concurrent environments, as it relies on the provider to encapsulate state per request. This can cause cross-request data leakage.
- gotcha The package is a fork of `react-helmet` specifically to address its thread-safety issues when performing asynchronous operations on the server. If migrating from `react-helmet`, ensure you replace all `Helmet.renderStatic()` calls with the `context` object extraction from `<HelmetProvider>`.
Install
-
npm install react-helmet-async -
yarn add react-helmet-async -
pnpm add react-helmet-async
Imports
- Helmet
import Helmet from 'react-helmet-async';
import { Helmet } from 'react-helmet-async'; - HelmetProvider
const { HelmetProvider } = require('react-helmet-async');import { HelmetProvider } from 'react-helmet-async'; - HelmetServerState (conceptual)
Helmet.renderStatic();
const { helmet } = helmetContext;
Quickstart
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Helmet, HelmetProvider } from 'react-helmet-async';
const App = () => (
<div>
<Helmet>
<html lang="en" amp />
<title>My Awesome App</title>
<meta name="description" content="A basic example of react-helmet-async." />
<link rel="canonical" href="https://example.com/" />
<style>{`body { background-color: #f0f0f0; }`}</style>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"url": "https://example.com/",
"name": "My Awesome App"
}
</script>
</Helmet>
<h1>Welcome to My Awesome App!</h1>
<p>Check the document head for meta tags.</p>
</div>
);
const rootElement = document.getElementById('app');
if (rootElement) {
createRoot(rootElement).render(
<HelmetProvider>
<App />
</HelmetProvider>
);
} else {
console.error('Root element #app not found!');
}