Common Mistakes & Gotchas
Avoid these frequent errors when setting up SEO in your React application.
1. Writing <html> and <head> in page components
Page components should never include document-level tags. These belong in the Document component only.
// pages/AboutPage.tsx — DO NOT DO THIS
export function AboutPage() {
const seo = mergeSEOConfig(siteConfig, { title: "About" });
return (
<html> {/* WRONG */}
<head> {/* WRONG */}
<SEOHead {...seo} />
</head>
<body> {/* WRONG */}
<h1>About</h1>
</body>
</html>
);
}// pages/AboutPage.tsx — CORRECT
export function AboutPage() {
const seo = mergeSEOConfig(siteConfig, { title: "About" });
return (
<Document seo={seo}>
<h1>About</h1>
</Document>
);
}Rule: If you are writing <html> or <head> inside a pages/ file, it is wrong. Only the Document file should have these.
2. Using <SEOHead> in Next.js App Router pages
Next.js App Router manages <head> automatically. Use generateMetadata instead.
// app/about/page.tsx — DO NOT DO THIS
export default function AboutPage() {
const seo = mergeSEOConfig(siteConfig, { title: "About" });
return (
<div>
<SEOHead {...seo} /> {/* WRONG in App Router */}
<h1>About</h1>
</div>
);
}// app/about/page.tsx — CORRECT
import { mergeSEOConfig, buildTitle } from "react-ssr-seo-toolkit";
import type { Metadata } from "next";
const seo = mergeSEOConfig(siteConfig, { title: "About" });
export const metadata: Metadata = {
title: buildTitle(seo.title!, seo.titleTemplate),
description: seo.description,
};
export default function AboutPage() {
return <div><h1>About</h1></div>;
}3. Forgetting to merge with site config
// Loses all site defaults!
const seo = createSEOConfig({
title: "About Us",
description: "About page.",
});
// Missing: titleTemplate, openGraph.siteName, twitter.site, etc.const seo = mergeSEOConfig(siteConfig, {
title: "About Us",
description: "About page.",
});
// Inherits titleTemplate, openGraph.siteName, twitter.site, etc.4. Not setting canonical URLs
Without canonical URLs, search engines may index duplicate content.
const seo = mergeSEOConfig(siteConfig, {
title: "About",
description: "About page.",
canonical: buildCanonicalUrl(SITE_URL, "/about"),
});5. Using JSON.stringify for JSON-LD
JSON.stringify can create XSS vulnerabilities with characters like <, >, &.
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>// Option 1: JsonLd component (recommended)
<JsonLd data={schema} />
// Option 2: safeJsonLdSerialize for manual rendering
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: safeJsonLdSerialize(schema) }}
/>6. Duplicate SEO tags
Do not render <SEOHead> in both the Document and the page.
// Document already renders <SEOHead />
export function AboutPage() {
return (
<Document seo={seo}>
<SEOHead {...seo} /> {/* DUPLICATE! */}
<h1>About</h1>
</Document>
);
}7. Hardcoding URLs
Hardcoded URLs cause trailing slash and protocol inconsistencies.
canonical: "https://mysite.com/about/" // Trailing slash
openGraph: { url: "http://mysite.com/about" } // Wrong protocolconst url = buildCanonicalUrl(SITE_URL, "/about");
// Consistent: no trailing slash, correct protocol
const seo = mergeSEOConfig(siteConfig, {
canonical: url,
openGraph: { url },
});8. SSR/CSR confusion
SEO tags must be server-rendered. In a client-side only app (Create React App, Vite SPA without SSR), search engines won't see your meta tags. You need SSR (Express, Next.js, React Router SSR, etc.) for this to work.
9. Missing Open Graph images
Without OG images, shared links look plain on social media.
openGraph: {
images: [{
url: "https://mysite.com/og-image.jpg",
width: 1200, // Recommended: 1200x630
height: 630,
alt: "Description of the image",
}],
}10. Not testing your SEO output
- View Page Source — Right-click → "View Page Source" to check the raw HTML
- Google Rich Results Test — Validate JSON-LD structured data
- Facebook Sharing Debugger — Test social sharing preview
- Twitter Card Validator — Preview Twitter Card appearance
- Google Search Console — Monitor indexing and errors
Quick reference: What goes where
| File | Should contain | Should NOT contain |
|---|---|---|
config/seo.ts | createSEOConfig, SITE_URL | Page-specific data |
Document.tsx | <html>, <head>, <SEOHead>, <JsonLd> | Page-specific content |
pages/*.tsx | mergeSEOConfig, schemas, content in <Document> | <html>, <head>, <SEOHead> |
server.tsx | renderToString, route matching | SEO config, meta tags |
Need more help? Check the FAQ or the API Reference.