React Helmet is great. It's also too much for a SPA with twelve dynamic blog routes. Here's what I shipped instead.
I run Inithouse , a portfolio of about 14 web products. All built as React SPAs. Every single one needs proper meta tags for SEO: title, description, Open Graph, Twitter cards, JSON-LD. The usual stuff.
I started with React Helmet. Worked fine for the first app. By app three, I was copy-pasting the same boilerplate helmet component across repos, fighting with nested helmet instances, and wondering why my bundle had an extra 12KB just to set a page title.
So I wrote a hook. About 20 lines. It replaced Helmet across every project.
The hook
import { useEffect } from ' react ' ;
interface SEOProps { title : string ; description ?: string ; image ?: string ; url ?: string ; type ?: string ; jsonLd ?: Record < string , unknown > ; }
export function useSEO ({ title , description , image , url , type = ' website ' , jsonLd }: SEOProps ) { useEffect (() => { document .…