General

What does react-ssr-seo-toolkit do?

It provides builder functions, React components, and JSON-LD schema generators to add complete SEO support to server-rendered React applications. It handles meta tags (title, description, canonical), Open Graph tags, Twitter Cards, JSON-LD structured data, hreflang links, and robots directives.

Does it work with client-side rendered (CSR) apps?

No, not for SEO purposes. Search engines read the initial HTML response. In a client-side only app (Create React App, Vite SPA without SSR), the HTML is mostly empty and JavaScript fills it in later. Search engines may not execute your JavaScript reliably. You need server-side rendering (SSR) for SEO meta tags to be effective. The toolkit is designed specifically for SSR.

What frameworks does it support?

It is framework-agnostic. It works with any setup that server-renders React, including:

  • Next.js — App Router and Pages Router
  • React Router 7 — with SSR mode
  • Remix
  • Express + React — custom SSR server
  • Astro — with React integration
  • Any Node.js server using renderToString or renderToPipeableStream

Does it have any dependencies?

Zero dependencies. The only peer dependency is React >= 18.0.0. There are no other runtime dependencies.

Is it TypeScript only?

No. The package is written in TypeScript and ships with complete type definitions, but it works perfectly in JavaScript projects too. You just don't get type checking and autocomplete without TypeScript.

Setup & Configuration

Where do I put the shared SEO config?

Create a config/seo.ts (or lib/seo.ts in Next.js) file at the root of your source directory. This file exports siteConfig (created with createSEOConfig) and SITE_URL. Every page imports from this file.

// config/seo.ts
import { createSEOConfig } from "react-ssr-seo-toolkit";

export const siteConfig = createSEOConfig({
  titleTemplate: "%s | My Site",
  openGraph: { siteName: "My Site" },
  twitter: { card: "summary_large_image" },
});

export const SITE_URL = "https://mysite.com";

Do I use this in the layout or in each page?

Both, but they do different things:

  • Document — Renders <SEOHead> and <JsonLd> inside <head>. This is where the actual HTML tags are generated.
  • Page — Calls mergeSEOConfig to build the SEO config and creates any JSON-LD schemas. Passes these to the Document as props.

The page decides what to render. The Document decides where to render it.

How do I reuse SEO config across all pages?

Use mergeSEOConfig(siteConfig, pageOverrides) in every page. The site config provides defaults (title template, site name, Twitter handle). The page only overrides what it needs (title, description, canonical). Everything else is inherited automatically.

What is the titleTemplate and how does %s work?

The titleTemplate is a string like "%s | My Site". When you set title: "About" on a page, buildTitle("About", "%s | My Site") produces "About | My Site". The %s is replaced with the page title. If no template is set, the title is used as-is.

Framework-Specific

How do I use this with Next.js App Router?

In Next.js App Router, do not use <SEOHead>. Instead, use the builder functions (mergeSEOConfig, buildTitle, buildCanonicalUrl) inside generateMetadata to produce Next.js's Metadata format. For JSON-LD, use safeJsonLdSerialize in the page component body. See the Next.js Guide for details.

Can I use <SEOHead> with Next.js Pages Router?

Yes. In the Pages Router, wrap <SEOHead> inside Next.js's <Head> component from next/head:

import Head from "next/head";
import { SEOHead } from "react-ssr-seo-toolkit";

<Head>
  <SEOHead {...seo} />
</Head>

How does it work with React Router 7?

Place <SEOHead> in your root.tsx layout. Route components return their SEO config via the loader function, and the root layout reads it via useMatches. See the Getting Started guide for the complete React Router example.

Does it work with Remix?

Yes. Remix and React Router 7 SSR use the same architecture. The root.tsx layout renders <SEOHead>, and route components provide SEO data via their loader functions.

Components & Functions

What is the difference between <SEOHead> and <JsonLd>?

<SEOHead> renders meta tags (title, description, OG, Twitter, canonical, robots, hreflang). <JsonLd> renders a single JSON-LD <script> tag for structured data. They serve different purposes and are typically used together in the Document.

When should I use the schema generators vs. the components?

Schema generators (createArticleSchema, createProductSchema, etc.) create plain JavaScript objects representing JSON-LD data. Components (<JsonLd>) render those objects as HTML <script> tags. Use the generators to create the data, then pass it to the component for rendering.

What does mergeSEOConfig do exactly?

It deep-merges two SEOConfig objects. The second argument overrides the first. Nested objects (like openGraph) are merged recursively. Arrays (like alternates, images) are replaced, not concatenated. This means you can set site-wide defaults and only override specific fields per page.

What does composeSchemas do?

composeSchemas(schema1, schema2, ...) combines multiple JSON-LD schema objects into a single object with a @graph array. Instead of multiple <script> tags, you get one clean <script> tag containing all schemas. Both approaches are valid for search engines.

Troubleshooting

My meta tags are not showing up in page source

Check these things in order:

  1. Are you using SSR? Meta tags must be server-rendered. View the page source (not DevTools Elements) to see what the server actually sends.
  2. Is <SEOHead> rendered inside <head> in your Document?
  3. Are you passing the correct config to <SEOHead>? Log it to verify.
  4. In Next.js App Router, are you using generateMetadata instead of <SEOHead>?

I see hydration mismatch errors

react-ssr-seo-toolkit is designed to produce deterministic output with no hydration mismatches. If you see hydration errors, check:

  • Are you using Date.now() or Math.random() in your SEO config? These produce different values on server and client.
  • Are you reading browser-only APIs (window.location) in the SEO config? Use server-provided values instead.
  • The toolkit itself does not access any browser globals — the issue is likely in your config values.

Google is not showing my JSON-LD rich results

Rich results (star ratings, FAQ dropdowns, etc.) are not guaranteed even with correct JSON-LD. Check:

  1. Validate your JSON-LD with Google's Rich Results Test tool
  2. Ensure all required fields are present for the schema type
  3. Google may take days to weeks to process and display rich results
  4. Not all schema types are eligible for rich results in all regions

Can I use this with Content Security Policy (CSP)?

Yes. Both <SEOHead> and <JsonLd> accept an optional nonce prop. Pass your CSP nonce to allow the JSON-LD script tags:

<SEOHead {...seo} nonce={cspNonce} />
<JsonLd data={schema} nonce={cspNonce} />