react-helmet-async

Thread-safe document head management for React 16 through 19, encapsulating title, meta, and link tags per request during server rendering.

Library
npm
v3.0.0
2,300 stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
44 /100 Fair
Development Activity 0
Maintenance 20
Community 56
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
69 /100 Good
Architecture 78
Code Quality 85
Innovation 72
Learning Curve 40

react-helmet-async is a fork of the original React Helmet built to fix its core weakness: thread-unsafe server rendering. Where react-helmet relies on react-side-effect’s static rewind() method — which breaks under concurrent, asynchronous SSR — this package requires a HelmetProvider that encapsulates head state per request via context, so multiple simultaneous server renders never leak each other’s title, meta, or link tags.

Starting with version 3.0, the library detects the React runtime version and adapts: on React 16-18 it keeps its original client/server dispatcher that diffs and patches <head> tags manually via direct DOM manipulation or server-side string/component generation, while on React 19+ it becomes a thin passthrough that renders <title>, <meta>, <link>, <style>, and <script> as ordinary JSX and lets React’s native head-hoisting take over, stepping in manually only for htmlAttributes and bodyAttributes, which React 19 does not hoist.

What You Get

  • Declarative <Helmet> component for title, meta, link, script, style, base, and html/body attribute tags
  • <HelmetProvider> with per-request context, making server-side rendering thread-safe under concurrent requests
  • Automatic React 19 detection that swaps to native JSX head-hoisting while preserving the same public API
  • prioritizeSeoTags flag to reorder canonical links, Open Graph, and Twitter Card tags ahead of less-important tags in server output
  • HelmetData escape hatch for using <Helmet> without React context, for isolated or parallel SSR renders

Common Use Cases

  • Setting per-page <title> and meta description/Open-Graph tags in a server-rendered React app
  • Injecting canonical link tags and JSON-LD structured data for SEO
  • Managing multiple concurrent SSR requests without title/meta state leaking between them
  • Adding a class to <html> or <body> conditionally from a nested route component
  • Migrating a React 16-18 codebase forward to React 19 without rewriting head-management call sites

Under The Hood

Architecture

react-helmet-async separates concerns across four layers: Helmet (src/index.tsx) is the public component that walks its React children and flattens them into a plain props object; Provider (src/Provider.tsx) supplies a HelmetData instance through context, scoping state to a single request/render tree instead of the module-level singleton react-side-effect relied on; Dispatcher (src/Dispatcher.tsx) is the reconciliation point that merges every mounted Helmet instance’s props via reducePropsToState (src/utils.ts) and hands the merged state to either client.ts (DOM diffing on the browser) or server.ts (string/React-element generation for SSR); and HelmetData (src/HelmetData.ts) owns the actual instance registry and context mutation. A parallel code path, React19Dispatcher.tsx, bypasses this whole pipeline on React 19+, rendering title/meta/link/script/style directly as JSX and relying on React’s native head-hoisting, falling back to manual DOM manipulation only for html/body attributes via a module-level react19Instances registry. The runtime switch is a single isReact19 check in both Helmet.render() and HelmetProvider, keeping the public API identical across both code paths. The main risk if either dispatcher’s merge/dedupe logic changes is silent divergence between the legacy and React-19 tag-priority behavior, since the two paths do not share reconciliation code.

Tech Stack

Written in TypeScript against the react peer dependency (16.6–19.x), with react-fast-compare for shouldComponentUpdate diffing, shallowequal for dispatcher prop comparisons, and invariant for runtime prop validation. Bundled via a custom esbuild-based build.ts script producing both CJS (lib/index.js) and ESM (lib/index.esm.js) outputs plus .d.ts declarations through a separate tsc --project tsconfig.build.json pass. Tests run on Vitest with @testing-library/react and jsdom for unit/integration coverage, and Playwright for a dedicated browser-based e2e suite (e2e/) alongside a server-rendering e2e config. Linting is ESLint (Remix’s shared config) plus Prettier, wired through Husky pre-commit hooks and commitlint for conventional commits, with CI running via GitHub Actions.

Code Quality

The test suite is extensive: 40 test files under __tests__/ (423 test/assertion call sites) split cleanly by concern — server/ for SSR output, react19/ for the native-JSX path, api/ for prop-level behavior, plus dedicated fragment, deferred, and misc suites. Error handling favors explicit invariant() calls that throw descriptive messages for invalid children rather than silently swallowing bad input. Naming is consistent (TAG_NAMES, ATTRIBUTE_NAMES, TAG_PROPERTIES enums centralize all magic strings in constants.ts), and the codebase is fully typed with no any-heavy interfaces beyond a few intentionally loose Props/Attributes maps needed for dynamic tag attributes. ESLint and Prettier are enforced via Husky pre-commit hooks, and CI runs the full suite on every push.

API Design The public surface is deliberately small: a single <Helmet> component and a <HelmetProvider>, with declarative children (<title>, <meta>, <link>, etc.) instead of a custom prop-based API, which keeps the learning curve low for anyone who already knows HTML. The library’s standout design decision is preserving that exact same API across a full internal rewrite for React 19 — consumers upgrading React versions get native head-hoisting for free without touching call sites, only losing access to context-based SSR state extraction and a few React 19-inapplicable flags (prioritizeSeoTags, titleTemplate behave as documented no-ops). The HelmetData escape hatch for context-free usage is a thoughtful addition for parallel/isolated SSR renders, a scenario the original react-helmet couldn’t handle at all.

Used by 14 apps in this directory

TypeScript
98%
Other

Novu

Developer Tools

40,195

Open-source communication infrastructure that connects your products and AI agents to every channel your users live on — Inbox, Email, SMS, Push, Chat, and more.

View details
93
Repo Health
80
Technical
64
Dependency
Built with
TypeScript 98%
Updated 5 days ago
TypeScript
98%
Other

Outline

Collaboration · Knowledge Management

41,045

A fast, real-time collaborative knowledge base for growing teams built on React, Node.js, and ProseMirror.

View details
91
Repo Health
87
Technical
68
Dependency
Built with
TypeScript 98%
Updated 5 days ago
TypeScript
99%
Other

Rowy

Databases · Low Code Platforms

6,842

Manage Firestore data in an Airtable-like spreadsheet UI and build Cloud Functions workflows directly in your browser—on your own GCP project.

View details
45
Repo Health
71
Technical
65
Dependency
Built with
TypeScript 99%
Updated 1 years ago
TypeScript
50%
Other

SigNoz

Analytics · Monitoring

32,429

Self-host your entire observability stack — logs, metrics, traces, and LLM monitoring — in one OpenTelemetry-native platform, without the Datadog bill.

View details
92
Repo Health
83
Technical
64
Dependency
Built with
TypeScript 50%
Go 38%
Updated 6 days ago
Go
94%
MIT

Traefik

Automation · Devops · Security

65,344

A cloud-native reverse proxy and load balancer that auto-configures itself from Docker, Kubernetes, and other orchestrators — zero manual routing required.

View details
93
Repo Health
85
Technical
64
Dependency
Built with
Go 94%
Updated 6 days ago

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers