Ahmad Alghawi
Goodbye react-helmet, hello React 19

2026-04-30 · 1 min read

Goodbye react-helmet, hello React 19

React 19 hoists <title>, <meta> and <link> tags into <head> automatically. Here is what changed in my portfolio when I dropped react-helmet-async.

react · react-19 · seo

The old way

For years the answer to "how do I set the page title in a SPA?" was react-helmet (or its async-safe sibling react-helmet-async). You wrapped your app in a <HelmetProvider> and rendered a <Helmet> element somewhere deep in your tree.

What React 19 changed

React 19 treats certain elements specially. If you render this anywhere in your component tree:

<title>{post.title} — My Site</title>
<meta name="description" content={post.excerpt} />
<link rel="canonical" href={canonical} />

…React hoists them straight into the document <head>, deduplicates by attribute, and even handles the async and defer flags on <script>.

Migration

For my portfolio it was a straight find-and-replace:

  1. Remove <HelmetProvider> from main.tsx.
  2. Replace every <Helmet>...</Helmet> with the bare children.
  3. npm uninstall react-helmet-async.

That's it. Same SEO output, one less dependency, no provider, no context.

Caveats

  • Server-rendering still needs a flush mechanism (React's official RSC story handles this).
  • For prerendered static pages, my custom scripts/prerender.ts injects the meta tags into the HTML shell directly so crawlers don't need JS at all.