How to dynamically update SEO meta tags in React using native DOM API without third-party libraries?

16 hours ago 1
ARTICLE AD BOX

Problem

I'm building a React app with Vite (not Next.js) and I need to manage SEO meta tags properly across all pages. I need:

A site-wide title template like %s | My Site applied automatically on every page

Per-page overrides for title, description, Open Graph, and Twitter Card

Robots directives like noindex for staging, and advanced ones like maxSnippet, maxImagePreview

Canonical URLs and hreflang for a multi-language site

Breadcrumb navigation with JSON-LD structured data for Google rich results

What I'm doing now

// ❌ Ad-hoc, inconsistent, no site-wide defaults function BlogPage({ post }) { return ( <> <title>{post.title} | My Site</title> <meta name="description" content={post.excerpt} /> <meta property="og:image" content={post.coverImage} /> <meta name="robots" content="index, follow" /> </> ); }

Problems with this approach

I have to manually append | My Site to every page title — if I rename the site, I update 50+ files.

No site-wide defaults — if I forget og:image on a page, it silently renders without one.

Raw strings for robots — typos like "noInDex" don't throw errors.

No JSON-LD breadcrumbs — I'm only rendering a visual <nav>, Google doesn't get structured data.

react-helmet is unmaintained, and next-seo only works with Next.js.

**What I want **

// Site-wide defaults once at app root <DefaultSEO titleTemplate="%s | My Site" defaultTitle="My Site" openGraph={{ type: 'website', siteName: 'My Site' }} twitter={{ site: '@mysite', cardType: 'summary_large_image' }} > <App /> </DefaultSEO> // Per-page override — only what's different <SEO title={post.title} description={post.excerpt} canonical={`https://example.com/blog/${post.slug}`} robots={{ index: true, follow: true, maxSnippet: 150, maxImagePreview: 'large' }} openGraph={{ type: 'article', images: [{ url: post.coverImage, width: 1200, height: 630 }], }} /> And for breadcrumbs with JSON-LD: tsx <Breadcrumb items={[ { name: 'Home', url: '/' }, { name: 'Blog', url: '/blog' }, { name: post.title }, ]} />
Read Entire Article