React Helmet Pro is an advanced, modular, and SSR compatible head manager for React applications. It now supports the familiar react-helmet / react-helmet-async API alongside its higher-level helpers for structured data, analytics, favicons, and security metadata.
Robust head management for SEO, analytics, and SSR made simple.
react-helmet-style child tag APIreact-helmet-async-styleHelmetProviderrequest context- Dynamic
<title>,<base>,<meta>,<link>,<script>,<style>,<noscript>injection - High-level
<Seo />component for common SEO tags <SiteSeo />for homepage metadata, site names, and organization identity<ArticleSeo />plus breadcrumb and FAQ rich-result helpershtmlAttributes,bodyAttributes, andtitleAttributestitleTemplate,defaultTitle,defer, andonChangeClientStateHelmet.renderStatic(),Helmet.peek(), andHelmetData- SEO tag prioritization for SSR via
prioritizeSeoTags - Next.js App Router helpers for
metadata,viewport,robots.ts,sitemap.ts, andmanifest.ts - JSON-LD Structured Data support
- Google Analytics integration
- Favicons & SEO helpers
- Security meta tags (CSP, nosniff, etc.)
- SSR-friendly with
collectHelmetTags() - Middleware support for reusable helmet logic
- Context API for global helmet state
- TypeScript support out of the box
# npm
npm install react-helmet-pro
# pnpm
pnpm add react-helmet-pro
# yarn
yarn add react-helmet-proThe new Next.js helpers are framework-agnostic utilities, so you can use them in a Next.js app without adding any extra runtime dependency from this package.
This table compares the documented feature surface of react-helmet-pro, react-helmet, react-helmet-async, and the Next.js App Router Metadata API.
Partial means the capability exists, but not as a first-class helper in that tool.
| Capability | react-helmet-pro |
react-helmet |
react-helmet-async |
Next.js Metadata API |
|---|---|---|---|---|
| Helmet-style child tag API | Yes | Yes | Yes | No |
| Thread-safe SSR context per request | Yes | No | Yes | Server-only metadata model |
Helmet.renderStatic() style extraction |
Yes | Yes | No | No |
HelmetData usage without a provider |
Yes | No | Yes | No |
prioritizeSeoTags SSR output |
Yes | No | Yes | No |
Next.js metadata / generateMetadata helper builders |
Yes | No | No | Built in |
Next.js viewport / generateViewport helper builders |
Yes | No | No | Built in |
Next.js robots.ts / sitemap.ts / manifest.ts builders |
Yes | No | No | Built in |
| JSON-LD helper component | Yes | No | No | Partial |
| High-level SEO helper component | Yes | No | No | No |
| Analytics helper component | Yes | No | No | No |
| Security meta helper component | Yes | No | No | Partial |
| Middleware hook for reusable head transforms | Yes | No | No | No |
| Built-in helper for reading live Helmet state | Yes | No | No | No |
<base>, <noscript>, inline <script>, inline <style> support through a Helmet API |
Yes | Yes | Yes | No in metadata config |
If you are using App Router, Next.js itself is the best fit for canonical SEO fields like title, description, Open Graph, Twitter cards, robots, sitemap, and manifest. react-helmet-pro is meant to complement that with helper builders, JSON-LD helpers, and a Helmet API for the head tags the Metadata API does not model directly.
import { HelmetProvider } from 'react-helmet-pro';
function App() {
return (
<HelmetProvider>
<MainRouter />
</HelmetProvider>
);
}import { Helmet } from 'react-helmet-pro';
<Helmet>
<title>About Us</title>
<meta name="description" content="Learn about our company" />
<meta name="keywords" content="company, team, about" />
<link rel="canonical" href="https://example.com/about" />
</Helmet>You can still use the prop-based shorthand if you prefer:
<Helmet
title="About Us"
meta={[{ name: 'description', content: 'Learn about our company' }]}
/>import { Seo } from 'react-helmet-pro';
<Seo
title="About Us"
description="Learn about our company"
canonical="https://example.com/about"
keywords={['company', 'team', 'about']}
openGraph={{
title: 'About Us',
type: 'website',
url: 'https://example.com/about',
images: [{ url: 'https://example.com/og/about.png', alt: 'About page preview' }],
}}
twitter={{
creator: '@example',
images: ['https://example.com/og/about.png'],
}}
/>import { SiteSeo } from 'react-helmet-pro';
<SiteSeo
title="React Helmet Pro"
description="Modern React head management with built-in SEO helpers."
canonical="https://reacthelmetpro.dev"
siteName="React Helmet Pro"
alternateSiteName={['Helmet Pro', 'RHP']}
openGraph={{
alternateLocale: ['de_DE'],
images: [{ url: 'https://reacthelmetpro.dev/og/home.png', alt: 'Homepage preview' }],
}}
organization={{
logo: 'https://reacthelmetpro.dev/logo.png',
sameAs: [
'https://github.com/opencorex-org/react-helmet-pro',
'https://www.npmjs.com/package/react-helmet-pro',
],
}}
/>import { ArticleSeo, BreadcrumbJsonLd, FAQJsonLd } from 'react-helmet-pro';
<>
<ArticleSeo
title="Shipping SEO in React"
description="A practical guide to richer article metadata and JSON-LD."
canonical="https://example.com/blog/shipping-seo"
authors={['Jane Doe']}
publishedTime="2026-05-01T12:00:00.000Z"
modifiedTime="2026-05-02T09:30:00.000Z"
images={[{ url: 'https://example.com/og/article.png', alt: 'Article cover' }]}
publisher={{ name: 'Acme', logo: 'https://example.com/logo.png' }}
schemaType="BlogPosting"
section="Guides"
tags={['SEO', 'React']}
/>
<BreadcrumbJsonLd
items={[
{ name: 'Home', item: 'https://example.com' },
{ name: 'Blog', item: 'https://example.com/blog' },
{ name: 'Shipping SEO in React', item: 'https://example.com/blog/shipping-seo' },
]}
/>
<FAQJsonLd
entries={[
{
question: 'How do I add rich-result schema?',
answer: 'Use the built-in helpers for breadcrumbs, FAQs, and article pages.',
},
]}
/>
</>import { StructuredData } from 'react-helmet-pro';
<StructuredData
json={{
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'React Helmet Pro Inc.',
url: 'https://reacthelmetpro.dev',
}}
/>For server-rendered frameworks like Next.js App Router, you can also render JSON-LD directly:
import { JsonLdScript } from 'react-helmet-pro';
<JsonLdScript
data={{
'@context': 'https://schema.org',
'@type': 'Article',
headline: 'Shipping SEO in Next.js',
}}
/>import { Analytics } from 'react-helmet-pro';
<Analytics type="gtag" id="G-XXXXXXXXXX" />You can define reusable middleware functions to extend or modify head data.
// middleware/withSiteSuffix.ts
export const withSiteSuffix = (head) => {
if (head.title) {
return { ...head, title: `${head.title} | My Awesome Site` };
}
return head;
};Apply it in your component:
import { useHelmetMiddleware } from 'react-helmet-pro';
import { withSiteSuffix } from './middleware/withSiteSuffix';
useHelmetMiddleware(withSiteSuffix);react-helmet-pro now supports both sides of modern Next.js SEO:
- App Router
metadata/generateMetadata viewport/generateViewport- metadata route files like
robots.ts,sitemap.ts, andmanifest.ts - JSON-LD rendering for server components
Helmetfor metadata fields that Next.js does not model directly, such as<base>,<noscript>, custom<script>, and custom<style>tags
Use this rule of thumb:
- Use
buildNextMetadata()for title, description, canonical URLs, Open Graph, Twitter, verification, robots, icons, alternates, app links, and web manifest URLs. - Use
buildNextViewport()for theme color and viewport settings. - Use
buildNextRobots(),buildNextSitemap(), andbuildNextManifest()inside metadata route files. - Use
JsonLdScriptfor server-rendered JSON-LD. - Use
Helmetonly for head tags the Next.js Metadata API does not support directly.
// app/layout.tsx
import type { Metadata } from 'next';
import { buildNextMetadata, buildNextViewport } from 'react-helmet-pro';
export const metadata: Metadata = buildNextMetadata({
metadataBase: 'https://acme.com',
defaultTitle: 'Acme',
titleTemplate: '%s | Acme',
description: 'Acme builds modern SEO tooling.',
alternates: {
canonical: '/',
languages: {
en: '/',
de: '/de',
},
},
openGraph: {
type: 'website',
siteName: 'Acme',
title: 'Acme',
description: 'Acme builds modern SEO tooling.',
images: ['/opengraph-image.png'],
},
twitter: {
card: 'summary_large_image',
creator: '@acme',
images: ['/twitter-image.png'],
},
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
'max-image-preview': 'large',
'max-snippet': -1,
'max-video-preview': -1,
},
},
verification: {
google: 'google-site-verification-token',
},
});
export const viewport = buildNextViewport({
width: 'device-width',
initialScale: 1,
colorScheme: 'light dark',
themeColor: [
{ media: '(prefers-color-scheme: light)', color: '#ffffff' },
{ media: '(prefers-color-scheme: dark)', color: '#111111' },
],
});// app/blog/[slug]/page.tsx
import type { Metadata } from 'next';
import { buildNextMetadata, JsonLdScript } from 'react-helmet-pro';
async function getPost(slug: string) {
return {
slug,
title: 'Shipping SEO in Next.js',
excerpt: 'How to combine metadata files, metadata exports, and JSON-LD.',
image: `/blog/${slug}/opengraph-image.png`,
};
}
export async function generateMetadata(
{ params }: { params: Promise<{ slug: string }> }
): Promise<Metadata> {
const { slug } = await params;
const post = await getPost(slug);
return buildNextMetadata({
title: post.title,
description: post.excerpt,
alternates: {
canonical: `/blog/${post.slug}`,
},
openGraph: {
title: post.title,
description: post.excerpt,
url: `/blog/${post.slug}`,
images: [post.image],
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.excerpt,
images: [post.image],
},
});
}
export default async function BlogPostPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
return (
<>
<JsonLdScript
data={{
'@context': 'https://schema.org',
'@type': 'Article',
headline: post.title,
description: post.excerpt,
}}
/>
<article>{post.title}</article>
</>
);
}// app/robots.ts
import type { MetadataRoute } from 'next';
import { buildNextRobots } from 'react-helmet-pro';
export default function robots(): MetadataRoute.Robots {
return buildNextRobots({
rules: {
userAgent: '*',
allow: '/',
disallow: '/private/',
},
sitemap: 'https://acme.com/sitemap.xml',
host: 'https://acme.com',
});
}// app/sitemap.ts
import type { MetadataRoute } from 'next';
import { buildNextSitemap } from 'react-helmet-pro';
export default function sitemap(): MetadataRoute.Sitemap {
return buildNextSitemap([
{
url: 'https://acme.com',
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 1,
alternates: {
languages: {
de: 'https://acme.com/de',
en: 'https://acme.com',
},
},
images: ['https://acme.com/opengraph-image.png'],
},
]);
}// app/manifest.ts
import type { MetadataRoute } from 'next';
import { buildNextManifest } from 'react-helmet-pro';
export default function manifest(): MetadataRoute.Manifest {
return buildNextManifest({
name: 'Acme',
short_name: 'Acme',
description: 'Acme builds modern SEO tooling.',
start_url: '/',
display: 'standalone',
background_color: '#ffffff',
theme_color: '#111111',
icons: [
{
src: '/icon-192.png',
sizes: '192x192',
type: 'image/png',
},
],
});
}Next.js does not model some head tags in metadata, including <base>, <noscript>, custom <script>, custom <style>, and certain resource hints. For those cases, use Helmet in a client component:
// app/components/LegacyHead.tsx
'use client';
import { Helmet } from 'react-helmet-pro';
export function LegacyHead() {
return (
<Helmet>
<base href="https://cdn.acme.com/" />
<noscript>{'<link rel="stylesheet" href="/noscript.css" />'}</noscript>
<script type="application/ld+json">
{'{"@context":"https://schema.org","@type":"WebSite"}'}
</script>
</Helmet>
);
}Wrap the app only if you want runtime Helmet updates or middleware support in client components:
// app/layout.tsx
import './globals.css';
import { HelmetProvider } from 'react-helmet-pro';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<HelmetProvider>{children}</HelmetProvider>
</body>
</html>
);
}// client/components/HeadWrapper.tsx
'use client';
import { useHelmetMiddleware } from 'react-helmet-pro';
import { withSiteSuffix } from '../middleware/withSiteSuffix';
export default function HeadWrapper() {
useHelmetMiddleware(withSiteSuffix);
return null;
}Use HeadWrapper at the top of your page/component to apply middleware.
metadata,generateMetadata,viewport, andgenerateViewportare server-only in App Router.- Prefer
metadata/generateMetadatafor canonical SEO fields, because they render on the server without a client boundary. - Use
JsonLdScriptfor server component JSON-LD andStructuredDataorHelmetwhen you specifically want head management behavior. - Use
Helmetonly for metadata the Next.js Metadata API does not support directly. - Avoid dynamic values like
Date.now()orMath.random()in client-managed head tags unless you intentionally snapshot them first.
Supports both child tags and prop shorthand.
| Prop | Type | Description |
|---|---|---|
children |
React nodes | Use <title>, <meta>, <link>, <script>, <style>, <noscript>, <html>, and <body> child tags |
title |
string |
Sets the page title |
meta |
MetaTag[] |
Adds meta tags |
link |
LinkTag[] |
Adds link tags |
script |
ScriptTag[] |
Adds script tags, including inline JSON-LD |
style |
StyleTag[] |
Adds inline style tags |
noscript |
NoscriptTag[] |
Adds noscript tags |
base |
BaseTag |
Adds a base tag |
htmlAttributes |
Record<string, string | boolean | number> |
Sets attributes on <html> |
bodyAttributes |
Record<string, string | boolean | number> |
Sets attributes on <body> |
titleAttributes |
Record<string, string | boolean | number> |
Sets attributes on <title> |
titleTemplate |
string |
Applies a title template like %s | My Site |
defaultTitle |
string |
Fallback title when no explicit title is set |
prioritizeSeoTags |
boolean |
Prioritizes SEO-relevant SSR tags in helmet.priority |
defer |
boolean |
Defers DOM updates with requestAnimationFrame |
onChangeClientState |
function | Receives newState, addedTags, and removedTags after client updates |
helmetData |
HelmetData |
Lets you collect state outside a provider, especially for SSR |
High-level SEO helper built on top of Helmet.
| Prop | Type | Description |
|---|---|---|
title |
string |
Sets the page title |
description |
string |
Standard meta description |
canonical |
string |
Canonical URL |
keywords |
string[] |
Keywords meta content (Google Search ignores this tag; retained for other consumers) |
defaultTitle |
string |
Fallback title when no page title is provided |
titleTemplate |
string |
Title template such as `%s |
author |
string |
Author meta content |
locale |
string |
Also used as <html lang> when no lang is already set |
siteName |
string |
Fallback Open Graph site name |
alternates |
SeoAlternateLink[] |
Hreflang and alternate links |
robots |
SeoRobotsDirectives |
Builds robots, googlebot, and googlebot-news meta tags, including indexifembedded |
prioritizeSeoTags |
boolean |
Places critical tags in Helmet's priority SSR output |
openGraph |
SeoOpenGraph |
Open Graph tags, including article metadata and image fields |
twitter |
SeoTwitter |
Twitter card tags |
verification |
SeoVerification |
Search engine/site verification tags |
jsonLd |
object | object[] |
Optional JSON-LD payloads rendered as script tags |
extraMeta |
MetaTag[] |
Extra meta tags to append |
extraLink |
LinkTag[] |
Extra link tags to append |
htmlAttributes |
HelmetAttributes |
Additional <html> attributes |
Homepage-focused helper built on top of Seo. It keeps page metadata aligned with WebSite and Organization JSON-LD so site names, brand identity, and social metadata stay in sync.
| Prop | Type | Description |
|---|---|---|
siteName |
string |
Preferred site name and Open Graph site name |
alternateSiteName |
string | string[] |
Alternate site names for WebSite JSON-LD |
organization |
organization input | Optional Organization JSON-LD details such as logo, sameAs, contactPoints, and address |
webSite |
website input | Optional overrides for WebSite JSON-LD fields |
jsonLd |
object | object[] |
Additional JSON-LD payloads to append |
other Seo props |
inherited | Includes canonical, description, keywords, locale, openGraph, twitter, and more |
SiteSeo is especially useful on a homepage or marketing landing page where you want:
- standard title, description, canonical, Open Graph, and Twitter tags
WebSitestructured data for site name signalsOrganizationstructured data for logo, same-as links, and contact details
Purpose-built helper for editorial pages. It renders the standard Seo tags, article Open Graph tags, and an Article or BlogPosting JSON-LD payload together.
| Prop | Type | Description |
|---|---|---|
title |
string |
Article headline and page title |
authors |
Array<string | { name, url? }> |
Author names or linked author descriptors |
publishedTime |
string |
article:published_time and JSON-LD publish date |
modifiedTime |
string |
article:modified_time and JSON-LD modified date |
expirationTime |
string |
Optional article:expiration_time |
images |
SeoImage[] |
Social preview images and schema image URLs |
publisher |
{ name, logo? } |
Publisher organization for JSON-LD |
schemaType |
'Article' | 'BlogPosting' | 'NewsArticle' |
Structured data type, defaults to Article |
section |
string |
Editorial section / category |
tags |
string[] |
Article tags for Open Graph |
jsonLd |
object | object[] |
Additional JSON-LD payloads to append |
other Seo props |
inherited | Includes canonical, description, keywords, locale, twitter, robots, verification, and more |
| Prop | Type | Description |
|---|---|---|
items |
Array<{ name: string; item: string }> |
Breadcrumb trail entries in order |
id |
string |
Optional script element id |
| Prop | Type | Description |
|---|---|---|
entries |
Array<{ question: string; answer: string }> |
FAQ question and answer pairs |
id |
string |
Optional script element id |
Client-friendly JSON-LD helper built on top of Helmet.
| Prop | Type | Description |
|---|---|---|
json |
object |
JSON-LD payload |
id |
string |
Optional script element id |
Server-safe JSON-LD renderer for frameworks like Next.js App Router.
| Prop | Type | Description |
|---|---|---|
data |
unknown |
JSON-LD payload |
type |
string |
Optional script type, defaults to application/ld+json |
id and other script props |
native script props | Passed through to the rendered <script> |
| Prop | Type | Description |
|---|---|---|
| organization fields | structured data fields | Renders Organization JSON-LD through StructuredData |
id |
string |
Optional script element id |
| Prop | Type | Description |
|---|---|---|
| website fields | structured data fields | Renders WebSite JSON-LD through StructuredData |
id |
string |
Optional script element id |
If you want to build the schema yourself and render it through StructuredData or JsonLdScript, the package also exports:
buildSchema()buildWebSiteSchema()buildOrganizationSchema()buildArticleSchema()buildBreadcrumbSchema()buildFaqSchema()
Example:
import { JsonLdScript, buildArticleSchema } from 'react-helmet-pro';
<JsonLdScript
data={buildArticleSchema({
headline: 'Shipping SEO in React',
type: 'BlogPosting',
authors: ['Jane Doe'],
url: 'https://example.com/blog/shipping-seo',
})}
/>| Prop | Type | Description |
|---|---|---|
href |
string |
Path to the favicon |
type |
string |
Optional MIME type |
sizes |
string |
Optional icon sizes |
| Prop | Type | Description |
|---|---|---|
type |
'gtag' |
Currently supports Google tag |
id |
string |
Your analytics id |
Injects a small set of security-oriented meta tags. The current built-in tag is:
<meta name="referrer" content="no-referrer" />
These helpers return plain objects that fit modern Next.js App Router SEO APIs.
| Export | Use for |
|---|---|
buildNextMetadata() |
metadata and generateMetadata() |
buildNextViewport() |
viewport and generateViewport() |
buildNextRobots() |
app/robots.ts |
buildNextSitemap() |
app/sitemap.ts |
buildNextManifest() |
app/manifest.ts |
safeJsonLdStringify() |
Sanitized JSON-LD serialization |
buildNextMetadata() covers the common SEO fields you usually need in App Router, including:
title,defaultTitle,titleTemplate, andabsoluteTitledescription,keywords,category,classification,referrermetadataBase,alternates,icons,manifestopenGraph,twitter,robots,verificationauthors,creator,publisherappleWebApp,appLinks,formatDetection,other
import { renderToString } from 'react-dom/server';
import { HelmetProvider } from 'react-helmet-pro';
const helmetContext = {};
renderToString(
<HelmetProvider context={helmetContext}>
<App />
</HelmetProvider>
);
const { helmet } = helmetContext;You can also collect SSR state without a provider:
import { Helmet, HelmetData } from 'react-helmet-pro';
const helmetData = new HelmetData({});
renderToString(
<Helmet helmetData={helmetData}>
<title>Standalone SSR</title>
</Helmet>
);
const { helmet } = helmetData.context;collectHelmetTags() can read from a provider context, a HelmetData instance, or an already-built server state:
import { collectHelmetTags } from 'react-helmet-pro';
const serverHelmet = collectHelmetTags(helmetContext);Test with Vitest + React Testing Library.
pnpm test- Contribution guide: CONTRIBUTING.md
- Security policy: SECURITY.md
- Code of conduct: CODE_OF_CONDUCT.md
Example test:
render(<Helmet title="Test Page" />);
expect(document.title).toBe("Test Page");We welcome all contributions! To get started:
git clone https://github.com/lahiruudayakumara/react-helmet-pro.git
cd react-helmet-pro
pnpm install
pnpm run devPlease open an issue or pull request if you find bugs or have feature requests.
- Email: udayakumara.wdl@gmail.com
- Website: https://lahiruudayakumara.com
Inspired by React Helmet, but rebuilt for modern apps with middleware, SSR, and context extensibility.