npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-ssr-seo-toolkit

v1.2.0

Published

Framework-agnostic SEO utilities, metadata builders, structured data helpers, and React components for SSR applications

Readme

react-ssr-seo-toolkit

The Complete SEO Toolkit for React SSR Applications

npm   TypeScript   License   Bundle

Meta TagsOpen GraphTwitter CardsJSON-LDCanonical URLsHreflangRobotsSitemapSEO ValidationOG Image

All in one package. Zero dependencies. Fully typed. SSR-safe.

Live Demo   |   Get Started   |   Examples   |   API   |   Frameworks


Why This Package?

The Problem

  • Most SEO packages are locked to Next.js
  • Many rely on browser-only APIs (window, document)
  • JSON-LD usually needs a separate package
  • No sitemap or robots.txt generation
  • No way to validate SEO completeness at build time

The Solution

  • Framework-agnostic — works everywhere
  • Zero browser globals — fully SSR-safe
  • JSON-LD built-in — 9 schema types including Person, Recipe, JobPosting
  • Sitemap + robots.txt generator built-in
  • SEO validation + scoring — catch missing tags at build time
  • OG image SVG generator — no external service needed
  • Social preview component — see how your page looks on Twitter, Facebook, etc.

Works With

| | Framework | Integration | |:---:|---|---| | | Next.js App Router | toNextMetadata() adapter → generateMetadata() | | | Next.js Pages Router | <SEOHead> inside next/head | | | React Router 7 | toRouterMeta() adapter → meta() export | | | Express + React SSR | <SEOHead> in renderToString() | | | Remix / Astro / Solid | Pure utility functions (no React needed) |


Get Started

1. Install

npm install react-ssr-seo-toolkit

Requires: react >= 18.0.0 as a peer dependency. Zero other dependencies.

2. Project Structure

The key idea: pages never write <html> or <head> tags — that's handled by a Document component, just like in Next.js or any modern React framework.

my-app/
├── config/
│   └── seo.ts              ← site-wide SEO defaults
├── components/
│   └── Document.tsx         ← handles <html>, <head>, <SEOHead>, <body>
├── pages/
│   ├── HomePage.tsx         ← just content + SEO config (no <html> tags!)
│   ├── AboutPage.tsx
│   └── BlogPost.tsx
├── server.tsx               ← Express / SSR entry point
└── package.json

3. Create Site Config (once)

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

export const siteConfig = createSEOConfig({
  titleTemplate: "%s | MySite",
  description: "Default site description for SEO.",
  openGraph: { siteName: "MySite", type: "website", locale: "en_US" },
  twitter: { card: "summary_large_image", site: "@mysite" },
});

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

3.5. Create a Document Component

// components/Document.tsx
import { SEOHead, JsonLd } from "react-ssr-seo-toolkit";
import type { SEOConfig } from "react-ssr-seo-toolkit";

interface DocumentProps {
  children: React.ReactNode;
  seo: SEOConfig;
  schemas?: Record<string, unknown>[];
}

export function Document({ children, seo, schemas }: DocumentProps) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
        <SEOHead {...seo} />
        {schemas?.map((schema, i) => <JsonLd key={i} data={schema} />)}
      </head>
      <body>
        <nav>{/* shared navigation */}</nav>
        <main>{children}</main>
        <footer>{/* shared footer */}</footer>
      </body>
    </html>
  );
}

4. Add to Any Page

// pages/AboutPage.tsx
import { mergeSEOConfig, buildCanonicalUrl } from "react-ssr-seo-toolkit";
import { siteConfig, SITE_URL } from "../config/seo";
import { Document } from "../components/Document";

export function AboutPage() {
  const seo = mergeSEOConfig(siteConfig, {
    title: "About Us",
    description: "Learn about our company and mission.",
    canonical: buildCanonicalUrl(SITE_URL, "/about"),
  });

  return (
    <Document seo={seo}>
      <h1>About Us</h1>
      <p>Our story...</p>
    </Document>
  );
}

That's it. Keep reading for sitemap generation, SEO validation, OG images, and more.


Real-World Examples

Sitemap Generation

// scripts/generate-sitemap.ts
import { generateSitemap } from "react-ssr-seo-toolkit/sitemap";
import { writeFileSync } from "fs";

const sitemap = generateSitemap({
  baseUrl: "https://trustix.uk",
  routes: [
    { path: "/", priority: 1.0, changefreq: "daily" },
    { path: "/tickets", priority: 0.9, changefreq: "hourly" },
    { path: "/about", priority: 0.5, changefreq: "monthly" },
    "/blog",
    "/contact",
  ],
  exclude: ["/dashboard/*", "/admin/*", "/login"],
  defaultChangefreq: "weekly",
  defaultPriority: 0.7,
});

writeFileSync("public/sitemap.xml", sitemap);

Output:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://trustix.uk</loc>
    <lastmod>2026-05-08</lastmod>
    <changefreq>daily</changefreq>
    <priority>1.0</priority>
  </url>
  ...
</urlset>

Tip: Call this in a build script or as an Express endpoint: res.type("application/xml").send(sitemap).


robots.txt Generation

import { generateRobots } from "react-ssr-seo-toolkit/sitemap";

const robots = generateRobots({
  rules: [
    {
      userAgent: "*",
      allow: "/",
      disallow: ["/dashboard", "/admin", "/login"],
    },
    {
      userAgent: "Googlebot",
      allow: "/",
      crawlDelay: 2,
    },
  ],
  sitemap: "https://trustix.uk/sitemap.xml",
});

Output:

User-agent: *
Allow: /
Disallow: /dashboard
Disallow: /admin
Disallow: /login

User-agent: Googlebot
Allow: /
Crawl-delay: 2

Sitemap: https://trustix.uk/sitemap.xml

Breadcrumb Auto-Generation from URL

import { autoBreadcrumb } from "react-ssr-seo-toolkit/sitemap";
import { createBreadcrumbSchema } from "react-ssr-seo-toolkit";

// /ticket/liverpool-vs-arsenal → breadcrumb items automatically
const items = autoBreadcrumb("/ticket/liverpool-vs-arsenal", {
  baseUrl: "https://trustix.uk",
  labels: { "/ticket": "Tickets" }, // optional custom label overrides
});

// Result:
// [
//   { name: "Home",                  url: "https://trustix.uk/" },
//   { name: "Tickets",               url: "https://trustix.uk/ticket" },
//   { name: "Liverpool Vs Arsenal",  url: "https://trustix.uk/ticket/liverpool-vs-arsenal" },
// ]

// Feed directly into schema:
const schema = createBreadcrumbSchema(items);

SEO Validation (build-time warnings)

Catch missing tags before they reach production:

import { validateSEO, printValidationReport } from "react-ssr-seo-toolkit/validation";

const issues = validateSEO([
  {
    path: "/",
    name: "HomePage",
    seo: { title: "Trustix UK", description: "Buy tickets online." },
  },
  {
    path: "/tickets",
    name: "TicketListPage",
    seo: {
      title: "Tickets",
      description: "Browse all tickets.",
      canonical: "https://trustix.uk/tickets",
      twitter: { card: "summary_large_image" },
      // og:image missing — will warn
    },
  },
]);

console.log(printValidationReport(issues));

Console output:

HomePage:
  ❌ [canonical] Missing canonical URL
  ⚠️  [og:image] Missing og:image
  ⚠️  [twitter:card] Missing twitter:card
  ⚠️  [structured-data] No structured data (JSON-LD)

TicketListPage:
  ⚠️  [og:image] Missing og:image
  ⚠️  [structured-data] No structured data (JSON-LD)

SEO Score per Page

import { getSEOScore, formatSEOScore } from "react-ssr-seo-toolkit/validation";

const result = getSEOScore(
  {
    title: "Liverpool vs Arsenal — Premier League Tickets",
    description: "Buy tickets for Liverpool vs Arsenal at Anfield.",
    canonical: "https://trustix.uk/tickets/liverpool-arsenal",
    openGraph: {
      images: [{ url: "https://trustix.uk/og/liverpool-arsenal.jpg" }],
    },
    twitter: { card: "summary_large_image" },
    jsonLd: { "@context": "https://schema.org", "@type": "Event" },
  },
  "TicketDetailPage"
);

console.log(formatSEOScore(result));

Console output:

🟢 SEO Score: TicketDetailPage  75/100 (75%)
  ✅ Title
  ✅ Description
  ✅ Canonical URL
  ✅ og:image
  ✅ og:title
  ✅ og:description
  ✅ Twitter Card
  ✅ Structured Data
  ❌ Robots Directives (-5) — No robots directives
  ❌ Hreflang (-5) — No hreflang alternates

OG Image Generation (no external service)

Generate 1200×630 SVG images server-side — no Puppeteer, no Vercel Edge, no API keys:

import { createOGImageSVG } from "react-ssr-seo-toolkit/og";

// Default template — dark gradient background
const svg = createOGImageSVG({
  title: "How to Build an SSR App",
  description: "A complete guide to server-rendered React.",
  brand: "My Blog",
  accentColor: "#6366f1",
});

// Serve from an Express endpoint:
app.get("/og/default.svg", (req, res) => {
  res.type("image/svg+xml").send(
    createOGImageSVG({ title: req.query.title as string, brand: "My Blog" })
  );
});

Article template (light background with category tag):

const svg = createOGImageSVG({
  title: "Premier League Preview 2026",
  description: "Everything you need to know about the upcoming season.",
  template: "article",
  category: "Sports",
  author: "James Walker",
  dateString: "May 8, 2026",
  brand: "Trustix UK",
  accentColor: "#e63946",
});

Sports event template (VS layout):

const svg = createOGImageSVG({
  title: "Liverpool vs Arsenal",
  template: "sports-event",
  homeTeam: "Liverpool",
  awayTeam: "Arsenal",
  eventDate: "Saturday, May 15, 2026 · 17:30",
  brand: "Trustix UK",
  accentColor: "#ffd700",
});

The SVG output can be used directly as og:image or converted to PNG with sharp:

import sharp from "sharp";
const png = await sharp(Buffer.from(svg)).png().toBuffer();

Social Preview Component (dev mode)

See exactly how your page will look on Twitter, Facebook, LinkedIn, and Google — without leaving the browser:

import { SEOPreview } from "react-ssr-seo-toolkit/components";

// Twitter card preview
<SEOPreview config={seoConfig} platform="twitter" />

// Facebook / Open Graph preview
<SEOPreview config={seoConfig} platform="facebook" />

// LinkedIn preview
<SEOPreview config={seoConfig} platform="linkedin" />

// Google search result preview
<SEOPreview config={seoConfig} platform="google" />

Tip: Wrap in {process.env.NODE_ENV === "development" && ...} so it never ships to production.


Blog / Article Page

import {
  mergeSEOConfig, buildCanonicalUrl,
  createArticleSchema, createBreadcrumbSchema,
} from "react-ssr-seo-toolkit";
import { siteConfig, SITE_URL } from "../config/seo";
import { Document } from "../components/Document";

export function BlogPostPage() {
  const seo = mergeSEOConfig(siteConfig, {
    title: "How to Build an SSR App",
    description: "A complete guide to building server-rendered React apps with proper SEO.",
    canonical: buildCanonicalUrl(SITE_URL, "/blog/ssr-guide"),
    openGraph: {
      title: "How to Build an SSR App",
      type: "article",
      url: "https://myblog.com/blog/ssr-guide",
      images: [{ url: "https://myblog.com/images/ssr-guide.jpg", width: 1200, height: 630, alt: "SSR Guide" }],
    },
    twitter: { title: "How to Build an SSR App", creator: "@authorhandle", image: "https://myblog.com/images/ssr-guide.jpg" },
  });

  const article = createArticleSchema({
    headline: "How to Build an SSR App",
    url: "https://myblog.com/blog/ssr-guide",
    datePublished: "2025-06-15",
    dateModified: "2025-07-01",
    author: [{ name: "Jane Doe", url: "https://myblog.com/authors/jane" }],
    publisher: { name: "My Blog", logo: "https://myblog.com/logo.png" },
    images: ["https://myblog.com/images/ssr-guide.jpg"],
    section: "Technology",
    keywords: ["React", "SSR", "SEO"],
  });

  const breadcrumbs = createBreadcrumbSchema([
    { name: "Home", url: "https://myblog.com" },
    { name: "Blog", url: "https://myblog.com/blog" },
    { name: "How to Build an SSR App", url: "https://myblog.com/blog/ssr-guide" },
  ]);

  return (
    <Document seo={seo} schemas={[article, breadcrumbs]}>
      <article>
        <h1>How to Build an SSR App</h1>
      </article>
    </Document>
  );
}

E-Commerce Product Page

import { mergeSEOConfig, buildCanonicalUrl, createProductSchema } from "react-ssr-seo-toolkit";

function ProductPage() {
  const url = buildCanonicalUrl(SITE_URL, "/products/ergonomic-keyboard");

  const seo = mergeSEOConfig(siteConfig, {
    title: "Ergonomic Mechanical Keyboard",
    description: "Premium split keyboard with Cherry MX Brown switches.",
    canonical: url,
    openGraph: {
      type: "product",
      url,
      images: [{ url: "https://acmestore.com/images/keyboard.jpg", width: 800, height: 800, alt: "Keyboard" }],
    },
  });

  const schema = createProductSchema({
    name: "Ergonomic Mechanical Keyboard",
    url,
    price: 189.99,
    priceCurrency: "USD",
    availability: "InStock",
    brand: "Acme Peripherals",
    sku: "ACME-KB-001",
    images: ["https://acmestore.com/images/keyboard.jpg"],
    ratingValue: 4.7,
    reviewCount: 342,
  });

  return (
    <Document seo={seo} schemas={[schema]}>
      <h1>Ergonomic Mechanical Keyboard</h1>
    </Document>
  );
}

Job Posting Schema

import { createJobPostingSchema } from "react-ssr-seo-toolkit";

const schema = createJobPostingSchema({
  title: "Senior React Engineer",
  description: "Build and maintain our SSR platform...",
  datePosted: "2026-05-01",
  validThrough: "2026-06-30",
  employmentType: "FULL_TIME",
  hiringOrganization: {
    name: "Trustix UK",
    sameAs: "https://trustix.uk",
    logo: "https://trustix.uk/logo.png",
  },
  jobLocation: { addressLocality: "London", addressCountry: "GB" },
  remote: true,
  baseSalary: {
    currency: "GBP",
    value: { minValue: 70000, maxValue: 95000 },
    unitText: "YEAR",
  },
});

Recipe Schema

import { createRecipeSchema } from "react-ssr-seo-toolkit";

const schema = createRecipeSchema({
  name: "Chicken Biryani",
  description: "Authentic Dhaka-style biryani with aromatic spices.",
  images: ["https://food.com/biryani.jpg"],
  author: { name: "Chef Rahman" },
  prepTime: "PT30M",
  cookTime: "PT1H",
  totalTime: "PT1H30M",
  recipeYield: "6 servings",
  recipeCategory: "Main Course",
  recipeCuisine: "Bengali",
  recipeIngredient: ["1kg chicken", "500g basmati rice", "onions"],
  recipeInstructions: [
    { name: "Marinate", text: "Marinate chicken with spices for 30 minutes." },
    { name: "Cook Rice", text: "Parboil basmati rice until 70% done." },
  ],
  ratingValue: 4.9,
  reviewCount: 128,
});

Person Schema

import { createPersonSchema } from "react-ssr-seo-toolkit";

const schema = createPersonSchema({
  name: "Tonmoy Khan",
  url: "https://tonmoykhan.site",
  jobTitle: "Software Engineer",
  image: "https://tonmoykhan.site/photo.jpg",
  sameAs: [
    "https://github.com/Tonmoy01",
    "https://linkedin.com/in/tonmoy",
  ],
  worksFor: { name: "Trustix UK", url: "https://trustix.uk" },
});

Event Page (Sports Match)

import { createEventSchema } from "react-ssr-seo-toolkit";

// @type auto-switches to "SportsEvent" when sport/homeTeam/awayTeam is provided
const schema = createEventSchema({
  name: "El Clásico",
  startDate: "2026-04-20T21:00:00+02:00",
  sport: "Soccer",
  homeTeam: { name: "FC Barcelona", url: "https://fcbarcelona.com" },
  awayTeam: { name: "Real Madrid CF", url: "https://realmadrid.com" },
  location: {
    name: "Camp Nou",
    address: { addressLocality: "Barcelona", addressCountry: "ES" },
  },
  eventStatus: "EventScheduled",
  eventAttendanceMode: "OfflineEventAttendanceMode",
});

Multi-Language (Hreflang)

const seo = mergeSEOConfig(siteConfig, {
  title: "Products",
  canonical: "https://mysite.com/products",
  alternates: [
    { hreflang: "en",        href: "https://mysite.com/en/products" },
    { hreflang: "es",        href: "https://mysite.com/es/products" },
    { hreflang: "fr",        href: "https://mysite.com/fr/products" },
    { hreflang: "x-default", href: "https://mysite.com/products" },
  ],
});

No-Index Pages

import { mergeSEOConfig, noIndex, noIndexNoFollow } from "react-ssr-seo-toolkit";

const loginSeo = mergeSEOConfig(siteConfig, {
  title: "Login",
  robots: noIndex(),            // "noindex, follow"
});

const adminSeo = mergeSEOConfig(siteConfig, {
  title: "Admin Dashboard",
  robots: noIndexNoFollow(),    // "noindex, nofollow"
});

Framework Integration

Next.js App Router

// app/blog/[slug]/page.tsx
import { toNextMetadata } from "react-ssr-seo-toolkit/adapters/nextjs";
import { createArticleSchema, safeJsonLdSerialize, buildCanonicalUrl } from "react-ssr-seo-toolkit";
import type { Metadata } from "next";

export async function generateMetadata({ params }): Promise<Metadata> {
  const post = await getPost(params.slug);
  return toNextMetadata({
    title: post.title,
    titleTemplate: "%s | My Blog",
    description: post.excerpt,
    canonical: buildCanonicalUrl("https://myblog.com", `/blog/${params.slug}`),
    openGraph: {
      title: post.title,
      type: "article",
      images: [{ url: post.image, width: 1200, height: 630, alt: post.title }],
    },
    twitter: { card: "summary_large_image" },
  });
}

React Router 7

// app/routes/about.tsx
import { toRouterMeta } from "react-ssr-seo-toolkit/adapters/react-router";
import { mergeSEOConfig, buildCanonicalUrl } from "react-ssr-seo-toolkit";
import { siteConfig, SITE_URL } from "../config/seo";
import type { Route } from "./+types/about";

export function meta({}: Route.MetaArgs) {
  return toRouterMeta(
    mergeSEOConfig(siteConfig, {
      title: "About Us",
      description: "Learn about our company.",
      canonical: buildCanonicalUrl(SITE_URL, "/about"),
      openGraph: { type: "website", title: "About Us" },
      twitter: { card: "summary_large_image" },
    })
  );
}

export default function AboutPage() {
  return <main><h1>About Us</h1></main>;
}

Express + React SSR

app.get("/sitemap.xml", (req, res) => {
  const { generateSitemap } = require("react-ssr-seo-toolkit/sitemap");
  res.type("application/xml").send(
    generateSitemap({ baseUrl: "https://mysite.com", routes: appRoutes, exclude: ["/admin/*"] })
  );
});

app.get("/robots.txt", (req, res) => {
  const { generateRobots } = require("react-ssr-seo-toolkit/sitemap");
  res.type("text/plain").send(
    generateRobots({ rules: { disallow: ["/admin"] }, sitemap: "https://mysite.com/sitemap.xml" })
  );
});

app.get("/og/:title.svg", (req, res) => {
  const { createOGImageSVG } = require("react-ssr-seo-toolkit/og");
  res.type("image/svg+xml").send(
    createOGImageSVG({ title: decodeURIComponent(req.params.title), brand: "MySite" })
  );
});

API Reference

Config Builders

| Function | What It Does | |---|---| | createSEOConfig(config?) | Create a normalized SEO config. Use for site-wide defaults. | | mergeSEOConfig(base, override) | Deep-merge site config with page-level overrides. | | normalizeSEOConfig(config) | Trim strings, normalize URLs, clean up a config object. |

Metadata Helpers

| Function | Example | Result | |---|---|---| | buildTitle(title, template) | buildTitle("About", "%s \| MySite") | "About \| MySite" | | buildCanonicalUrl(base, path) | buildCanonicalUrl("https://x.com", "/about") | "https://x.com/about" | | buildRobotsDirectives(config) | buildRobotsDirectives({ index: false }) | "noindex, follow" | | noIndex() | — | { index: false, follow: true } | | noIndexNoFollow() | — | { index: false, follow: false } |

JSON-LD Schema Generators

All return { "@context": "https://schema.org", "@type": "...", ... }.

| Function | Schema Type | Use Case | |---|---|---| | createOrganizationSchema(input) | Organization | Company info, logo, social links, contact | | createWebsiteSchema(input) | WebSite | Site name, sitelinks searchbox | | createArticleSchema(input) | Article | Blog posts, news articles | | createProductSchema(input) | Product | E-commerce: price, brand, SKU, ratings | | createBreadcrumbSchema(items) | BreadcrumbList | Navigation hierarchy | | createFAQSchema(items) | FAQPage | Question + answer pairs | | createEventSchema(input) | Event / SportsEvent | Concerts, conferences, sports — auto-switches to SportsEvent when sport/homeTeam/awayTeam is set | | createPersonSchema(input) | Person | Author bios, team pages | | createRecipeSchema(input) | Recipe | Food blogs, cooking sites | | createJobPostingSchema(input) | JobPosting | Career pages, job boards | | composeSchemas(...schemas) | @graph | Combine multiple schemas into one <script> |

Sitemap & Robots — react-ssr-seo-toolkit/sitemap

| Function | What It Does | |---|---| | generateSitemap(options) | Generates a valid sitemap.xml string from a list of routes | | generateRobots(options) | Generates a robots.txt string with user-agent rules and sitemap links | | autoBreadcrumb(path, options?) | Auto-generates BreadcrumbItem[] from a URL path like /ticket/liverpool-vs-arsenal |

generateSitemap options:

| Option | Type | Description | |---|---|---| | routes | string[] \| SitemapRoute[] | Route paths or route objects with lastmod, changefreq, priority | | baseUrl | string | Base URL (e.g. "https://mysite.com") | | exclude | string[] | Glob patterns to exclude (supports * wildcard and /* suffix) | | defaultChangefreq | ChangeFreq | Default change frequency for all routes | | defaultPriority | number | Default priority (0.0–1.0) for all routes |

SEO Validation & Scoring — react-ssr-seo-toolkit/validation

| Function | What It Does | |---|---| | validateSEO(routes) | Checks a list of routes for missing/invalid SEO fields, returns SEOValidationIssue[] | | printValidationReport(issues) | Formats issues into a human-readable string with icons | | getSEOScore(config, pageName?) | Scores a single page's SEO config out of 100 | | formatSEOScore(result) | Formats the score result as a pretty-printed string |

Scoring breakdown (100 points total):

| Check | Points | |---|---| | Title (≤60 chars) | 20 | | Description (≤160 chars) | 15 | | Canonical URL | 10 | | og:image | 15 | | og:title | 5 | | og:description | 5 | | Twitter Card | 10 | | Structured Data (JSON-LD) | 10 | | Robots Directives | 5 | | Hreflang | 5 |

OG Image Generation — react-ssr-seo-toolkit/og

| Function | What It Does | |---|---| | createOGImageSVG(options) | Returns a 1200×630 SVG string — serve as image/svg+xml or convert to PNG with sharp |

Options:

| Option | Type | Description | |---|---|---| | title | string | Main headline | | description | string? | Subtitle / description | | template | "default" \| "article" \| "sports-event" | Visual template | | brand | string? | Brand name shown in corner | | backgroundColor | string? | Override background color | | textColor | string? | Override text color | | accentColor | string? | Override accent / highlight color | | category | string? | Category tag (article template) | | author | string? | Author name (article template) | | dateString | string? | Display date (article template) | | homeTeam / awayTeam | string? | Team names (sports-event template) | | eventDate | string? | Event date display (sports-event template) |

React Components

<SEOHead>

Renders all SEO tags inside <head>. Accepts the full SEOConfig plus an optional nonce for CSP.

<SEOHead
  title="My Page"
  titleTemplate="%s | MySite"
  description="Page description."
  canonical="https://mysite.com/page"
  robots={{ index: true, follow: true }}
  openGraph={{ type: "website", images: [{ url: "...", width: 1200, height: 630 }] }}
  twitter={{ card: "summary_large_image", site: "@mysite" }}
/>

<JsonLd>

Standalone JSON-LD <script> tag renderer.

<JsonLd data={createProductSchema({ name: "Widget", url: "...", price: 29.99 })} />

<SEOPreview>react-ssr-seo-toolkit/components

Renders a visual social card preview. Useful in dev mode or a CMS preview panel.

<SEOPreview config={seoConfig} platform="twitter" />    // Twitter card
<SEOPreview config={seoConfig} platform="facebook" />   // Facebook / OG
<SEOPreview config={seoConfig} platform="linkedin" />   // LinkedIn post
<SEOPreview config={seoConfig} platform="google" />     // Google search result

| Prop | Type | Default | |---|---|---| | config | SEOConfig | required | | platform | "twitter" \| "facebook" \| "linkedin" \| "google" | "twitter" | | style | React.CSSProperties | — |

Framework Adapters

react-ssr-seo-toolkit/adapters/nextjs

| Function | What It Does | |---|---| | toNextMetadata(config) | Converts SEOConfig → Next.js App Router Metadata object | | buildNextTitle(config) | Returns the resolved title string |

react-ssr-seo-toolkit/adapters/react-router

| Function | What It Does | |---|---| | toRouterMeta(config) | Converts SEOConfigMetaDescriptor[] for a route's meta() export | | toRouterLinks(config) | Converts canonical + hreflang → LinkDescriptor[] for a route's links() export |

Utilities

| Function | What It Does | |---|---| | safeJsonLdSerialize(data) | Serialize JSON-LD safely — escapes <, >, & to prevent XSS | | normalizeUrl(url) | Trim whitespace, remove trailing slashes | | buildFullUrl(base, path?) | Combine base URL with path | | omitEmpty(obj) | Remove keys with undefined, null, or "" values | | deepMerge(base, override) | Deep-merge two objects (arrays replaced, not concatenated) |

TypeScript Types

import type {
  SEOConfig,
  OpenGraphConfig, OpenGraphImage, OpenGraphType,
  TwitterConfig, TwitterCardType,
  RobotsConfig,
  AlternateLink,
  JSONLDBase, BreadcrumbItem,
  OrganizationSchemaInput, WebsiteSchemaInput,
  ArticleSchemaInput, ProductSchemaInput,
  FAQItem,
  EventSchemaInput, EventStatus, EventAttendanceMode,
  PersonSchemaInput,
  RecipeSchemaInput, RecipeInstruction,
  JobPostingSchemaInput,
  // Sitemap
  SitemapRoute, GenerateSitemapOptions, ChangeFreq,
  RobotsRule, GenerateRobotsOptions,
  // Validation
  SEOValidationIssue, RouteWithSEO,
  SEOScoreResult, SEOScoreCheck,
  // OG Image
  OGImageOptions, OGImageTemplate,
  // Components
  SEOHeadProps, JsonLdProps, SEOPreviewProps,
} from "react-ssr-seo-toolkit";

Entry Points

The package ships separate entry points so you only import what you need:

| Import | Contents | |---|---| | react-ssr-seo-toolkit | Everything — core, schema, utils, components | | react-ssr-seo-toolkit/schema | Schema generators only (no React) | | react-ssr-seo-toolkit/sitemap | generateSitemap, generateRobots, autoBreadcrumb | | react-ssr-seo-toolkit/validation | validateSEO, getSEOScore, formatSEOScore | | react-ssr-seo-toolkit/og | createOGImageSVG | | react-ssr-seo-toolkit/components | React components — SEOHead, JsonLd, SEOPreview | | react-ssr-seo-toolkit/adapters/nextjs | Next.js adapter | | react-ssr-seo-toolkit/adapters/react-router | React Router 7 adapter |


Live Demo

git clone https://github.com/Tonmoy01/react-ssr-seo-toolkit.git
cd react-ssr-seo-toolkit
npm install
npm run demo

Then visit http://localhost:3000.


Development

npm install          # install dependencies
npm run build        # build the library
npm run dev          # watch mode (auto-rebuild)
npm test             # run tests
npm run lint         # type check
npm run demo         # run demo server

Troubleshooting

"Cannot find module 'react-ssr-seo-toolkit'"

Ensure the package is installed and your bundler supports the exports field in package.json.

Hydration mismatch warnings

<SEOHead> produces deterministic output. Ensure the same config object is used on both server and client. Avoid using Date.now() or random values in your SEO config.

JSON-LD not appearing in page source

Make sure <JsonLd> is inside <head> and rendered during SSR — not in a client-only useEffect.


Contributing

  1. Fork the repo
  2. Create a feature branch — git checkout -b feature/my-feature
  3. Make your changes with tests
  4. Run npm test && npm run lint
  5. Open a PR

MIT License • Made by Tonmoy