NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm, #4312 most downloaded on npm
Thread-safe Helmet for React 16–18, with native support for React 19+
Latest stable
3.1.0
Last release 3 days ago
11 Nov 2026
Release timing varies
gaps range from 1 weeks to 1.8 years
Rarely documented
notes for 8 of 33 stable releases
Nothing withdrawn
no release was ever pulled
9 years old
59 releases · first in 2018
Drag across the ruler to compare, or tap a release to open it.
react-helmet-async/package.json resolves again. The exports map added in 3.0.0 had left it out.
react-helmet-async/package.json resolves again. The exports map added in 3.0.0 had left it out.<Helmet> instances are merged again, as on React 16–18: the innermost <title> wins and uses the nearest titleTemplate and defaultTitle, and a tag that several <Helmet>s set, such as <meta name="description">, renders once, from the innermost one. Server rendering still outputs every <Helmet>'s tags, because instances only merge once they mount, and when hydrating React reuses only the first server-rendered <title>, so a second one stays in <head>.prioritizeSeoTags, <meta charset> moves from helmet.meta to the start of helmet.priority, because browsers only honor a charset declared within the first 1024 bytes of the document. A template that prints helmet.meta but not helmet.priority no longer outputs it.<Helmet> throws for invalid children or a missing <HelmetProvider> are plain Errors, named Error instead of Invariant Violation.invariant and shallowequal dependencies.<Helmet> with no <HelmetProvider> above it and no helmetData prop throws <Helmet> must be rendered inside a <HelmetProvider> or be given a helmetData prop. If it is, check that only one copy of react-helmet-async is bundled. instead of TypeError: Cannot read properties of undefined (reading 'add').meta, link, script, style and noscript props that use React attribute names such as itemProp, charSet or httpEquiv are no longer dropped. Their React names are written as HTML attribute names, as for JSX children, so httpEquiv becomes http-equiv and className becomes class in the DOM and in toString().<link>, <meta>, <noscript>, <script> and <style> tags inside a fragment are no longer dropped when <Helmet> has more than one fragment, or a fragment next to a tag of the same type.onLoad are attached as event handlers instead of being written as attributes that contain the function's source, and toString() leaves them out. Handlers receive the native Event, not a React synthetic event.lib/index.js and lib/index.esm.js are compiled to ES2018, so webpack 4, which Create React App 3 and 4 use, can parse them. Since 2.0.0 they contained class fields and optional chaining.defer on (the default), changes made while the tab is in the background are applied within about a second instead of when the tab is shown again.<base> with only a target attribute is no longer dropped. The HTML spec allows href, target, or both.Full changes: https://github.com/staylor/react-helmet-async/compare/ba5d5fb820750dcf29c9621e9feafe2b8388d60b...v3.1.0
Your coding agent can read these notes before it upgrades. Set up the MCP server →
React 19 support. The react peer dependency range is now ^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0. When React.version is 19 or later, renders , , , ,
react peer dependency range is now ^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0. When React.version is 19 or later, <Helmet> renders <title>, <meta>, <link>, <base>, <script>, <style> and <noscript> as React elements and leaves their placement to React. React 16–18 keep the 2.x behavior.exports map in package.json: import resolves to lib/index.esm.js and require to lib/index.js.<Helmet> and <HelmetProvider> no longer collect state or manage <head> themselves:
<HelmetProvider> only renders its children. Its context object is never filled in, so server code that reads helmetContext.helmet gets undefined. The helmetData prop is ignored too, and helmetData.context.helmet keeps its initial empty value.htmlAttributes and bodyAttributes are applied to the DOM after mount, so server-rendered HTML doesn't include them.onChangeClientState is never called.prioritizeSeoTags has no effect.<Helmet> instances are no longer merged or deduplicated against each other. Nested <Helmet>s that each set a title, a <meta name="description"> or a canonical <link> render two of each. Only React's own resource deduplication applies: it drops repeated async scripts with the same src and stylesheets with the same href and precedence.titleTemplate and defaultTitle only apply to the <Helmet> that sets them. An app-level <Helmet titleTemplate="%s | Site" defaultTitle="Site" /> plus a page-level <Helmet title="About" /> renders <title>Site</title> and <title>About</title> instead of About | Site.<title>, <meta> and <link> tags into <head>, or to the start of the output when it renders only part of the page. <base>, <noscript> and <meta itemprop> tags are rendered where the <Helmet> is, and so are <script>, <style> and stylesheet <link> tags unless they use React 19's opt-ins: async and src on scripts, precedence on stylesheets, href and precedence on styles.exports map exposes only the package root. Deep imports such as react-helmet-async/lib/index.js or react-helmet-async/package.json no longer resolve in Node.js (ERR_PACKAGE_PATH_NOT_EXPORTED) or in bundlers that honor exports.import without a bundler) now loads lib/index.esm.js instead of the CommonJS build. Imports of the package root through require() or a bundler are unaffected.
"type": "module", so Node.js runs that file as ESM only through module syntax detection, which is on by default from Node.js 20.19 and 22.7. On older versions every import of the package throws a SyntaxError. The package still declares no engines field.import { Helmet } from 'react-helmet-async' work again; they failed in 2.x. The ESM build has no default export, though, so the 2.x workaround import pkg from 'react-helmet-async' throws on every Node.js version, and TypeScript doesn't catch it.HelmetDatum.toComponent() returns React.ReactElement[] instead of React.Component<any>, so the server state's toComponent() results can be rendered in JSX without a cast.HelmetData#context.helmet is typed HelmetServerState | null. It becomes null after a client-side render.Helmet's internal methods, such as mapChildrenToProps, are private.HelmetServerState.titleAttributes. It was never set at runtime; title attributes are rendered as part of helmet.title.MappedServerState type export.helmet.priority.toComponent() returns an empty array instead of undefined when prioritizeSeoTags is off.@types/react 19 without skipLibCheck. The 2.x declarations used the global JSX namespace, which @types/react 19 removed.Full changes: https://github.com/staylor/react-helmet-async/compare/5f4da3896c8b70bedcd3c05d406ff5241601933c...ba5d5fb820750dcf29c9621e9feafe2b8388d60b
This project publishes no release notes. Between 0.2.0 and v3.0.0 there were 131 commits, 93 of them substantive:
…and 73 more.
react-dom from peerDependencies.
react-dom from peerDependencies.Nothing published for this version
Nothing published for this version
Nothing published for this version
The HelmetProvider constructor's props type includes children.
HelmetProvider constructor's props type includes children.Nothing published for this version
Nothing published for this version
Nothing published for this version
No changes. The published files are identical to 2.0.2 apart from the version number.
No changes. The published files are identical to 2.0.2 apart from the version number.
Nothing published for this version
Nothing published for this version
Nothing published for this version
's context prop accepts an object without helmet, such as {}, in the type declarations.
<HelmetProvider>'s context prop accepts an object without helmet, such as {}, in the type declarations.Nothing published for this version
HelmetProvider.canUseDOM requires window.document.createElement to exist, instead of only a global document.
HelmetProvider.canUseDOM requires window.document.createElement to exist, instead of only a global document.<HelmetProvider>'s context prop is optional again in the type declarations.Nothing published for this version
The package was rewritten in TypeScript and is built with esbuild instead of Babel and microbundle. It still declares no engines field; the .nvmrc bum
The package was rewritten in TypeScript and is built with esbuild instead of Babel and microbundle. It still declares no engines field; the .nvmrc bump to Node.js 18.17.1 only applies to developing this repository.
Attributes, MappedServerState, StateUpdate, TagList and TitleProps."sideEffects": false in package.json.lib/index.js (CommonJS) and lib/index.esm.js (ES modules) are no longer compiled to ES5. They contain ES2022 syntax such as class fields, optional chaining and object spread. Loading them needs Node.js 14 or later (1.3.0 ran on Node.js 12), and projects that target older browsers, or use tools that can't parse that syntax, need to transpile the package.lib/index.module.js to lib/index.esm.js, and src/ and the root index.d.ts are no longer published. Deep imports of those paths break.import { Helmet } from 'react-helmet-async' throws SyntaxError: Named export 'Helmet' not found, because Node.js can't see the CommonJS build's named exports. Use import pkg from 'react-helmet-async' or require() instead. Bundlers are unaffected.index.d.ts, and some are stricter:
base, titleAttributes, script, style and noscript take Attributes ({ [key: string]: string }) instead of any or Object, so non-string values such as script={[{ src, async: true }]} no longer type-check.<HelmetProvider> requires a context prop that contains helmet. 2.0.1 and 2.0.2 relax this again.prioritizeSeoTags also moves <meta name="generator"> into helmet.priority.lib/index.umd.js and the umd:main field) and lib/index.modern.js.FilledContext, ProviderProps and OtherElementAttributes type exports.Helmet and HelmetProvider no longer have propTypes, and prop-types and @babel/runtime are no longer dependencies.Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
This project publishes no release notes. Between 0.2.0-beta.0 and 0.2.0 there were 14 commits, 10 of them substantive:
This project publishes no release notes. Between 0.2.0-beta.0 and 0.2.0 there were 14 commits, 10 of them substantive:
Nothing published for this version
This project publishes no release notes. Between 0.1.0 and 0.2.0-beta.0 there were 18 commits, 12 of them substantive:
This project publishes no release notes. Between 0.1.0 and 0.2.0-beta.0 there were 18 commits, 12 of them substantive:
This project publishes no release notes. Between v0.0.5 and 0.1.0 there were 7 commits, 5 of them substantive:
This project publishes no release notes. Between v0.0.5 and 0.1.0 there were 7 commits, 5 of them substantive:
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
One column per quarter.