@masters-ws/react-seo
v1.5.4
Published
Professional high-performance SEO package for React/Next.js. Zero-dependency core for Next.js App Router, optional Helmet components for React.
Maintainers
Readme
@masters-ws/react-seo
Professional SEO toolkit for React & Next.js — SSR-first, zero-dependency core, 30+ schema types, and built-in development auditor.
Why @masters-ws/react-seo?
Most SEO packages inject metadata client-side — after JavaScript loads. Google crawls your page before that. This package fixes that.
| Feature | @masters-ws/react-seo | next-seo | react-helmet |
|---|---|---|---|
| Zero-dependency core | ✅ | ❌ | ❌ |
| SSR-first (App Router) | ✅ | ✅ | ❌ |
| @graph JSON-LD | ✅ | ❌ | ❌ |
| One-call page setup | ✅ | ❌ | ❌ |
| Integrated SEO Auditor | ✅ | ❌ | ❌ |
| Built-in pagination SEO | ✅ | partial | ❌ |
Installation
# Next.js App Router (zero dependencies)
npm install @masters-ws/react-seo
# React / Next.js Pages Router
npm install @masters-ws/react-seo react-helmet-async🔍 SEO Auditor (Development Only)
This library includes a built-in SEO Auditor component designed to speed up your development workflow by providing a real-time analysis of your page's SEO status directly in your browser.
- Real-Time Analysis: Scans for title/description lengths, H1 counts, missing alt tags, and more.
- Visual Previews: Live mockups for Google Search results and Social Media cards.
- Production Safe: Only renders when
process.env.NODE_ENV === 'development'.
Usage:
// app/layout.tsx (Next.js App Router)
import { SEOAuditor } from '@masters-ws/react-seo';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<SEOAuditor />
</body>
</html>
);
}🚀 Quick Start: Next.js App Router (Recommended)
Product Page — One-Call Setup
// app/products/[slug]/page.tsx
import { generateProductMetadata, JsonLd } from '@masters-ws/react-seo/core';
const siteConfig = {
name: "My Store",
url: "https://store.com",
logo: "https://store.com/logo.png",
description: "Best online store",
language: "en_US",
};
export async function generateMetadata({ params }) {
const product = await fetchProduct(params.slug);
const { metadata } = generateProductMetadata({
name: product.name,
description: product.short_description,
image: [product.main_image, ...product.gallery],
price: product.price,
currency: "USD",
sku: product.sku,
brand: product.brand?.name,
availability: product.in_stock
? "https://schema.org/InStock"
: "https://schema.org/OutOfStock",
url: `https://store.com/products/${params.slug}`,
breadcrumbs: [
{ name: "Home", item: "https://store.com" },
{ name: product.category?.name, item: `https://store.com/categories/${product.category?.slug}` },
{ name: product.name, item: `https://store.com/products/${params.slug}` },
],
}, siteConfig);
return metadata;
}
export default async function ProductPage({ params }) {
const product = await fetchProduct(params.slug);
const { schemas } = generateProductMetadata({ /* same options */ }, siteConfig);
return (
<>
<JsonLd schema={schemas} graph />
<ProductDetailClient product={product} />
</>
);
}What Google sees in the HTML source:
<title>Product Name | My Store</title>
<meta name="description" content="..." />
<meta property="og:title" content="Product Name" />
<meta property="og:image" content="https://store.com/product.jpg" />
<link rel="canonical" href="https://store.com/products/my-product" />
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{ "@type": "Product", "name": "...", "offers": {...} },
{ "@type": "BreadcrumbList", "itemListElement": [...] },
{ "@type": "Organization", "name": "..." },
{ "@type": "WebSite", "potentialAction": {...} }
]
}
</script>Core Functions
One-Call Helpers (Recommended)
| Function | Generates |
|---|---|
| generateProductMetadata(product, config) | Metadata + Product, Breadcrumb, Organization, WebSite schemas |
| generateArticleMetadata(article, config) | Metadata + NewsArticle, Breadcrumb, Organization, WebSite schemas |
| generateCategoryMetadata(category, config) | Metadata + CollectionPage, Breadcrumb, ItemList schemas |
| generateHomepageMetadata(input, config) | Metadata + WebPage, Organization, WebSite, optional LocalBusiness schemas |
Schema Generators
| Function | Schema Type |
|---|---|
| generateProductSchema(data) | Product — multi-image, reviews, return policy, shipping, variants |
| generateArticleSchema(data, config) | NewsArticle |
| generateFAQSchema(questions) | FAQPage |
| generateBreadcrumbSchema(items) | BreadcrumbList |
| generateLocalBusinessSchema(data) | LocalBusiness |
| generateOrganizationSchema(config) | Organization |
| generateWebSiteSchema(config) | WebSite with SearchAction |
| generateWebPageSchema(data, config) | WebPage |
| generateHowToSchema(data) | HowTo |
| generateRecipeSchema(data) | Recipe |
| generateJobPostingSchema(data) | JobPosting |
| generateEventSchema(data) | Event |
| generateVideoSchema(data) | VideoObject |
Utilities
| Function | Description |
|---|---|
| toNextMetadata(data, config) | Converts SEO data to Next.js Metadata object |
| cleanSchema(obj) | Removes undefined/null from schema objects |
| validateSEO(type, data, fields) | Dev-only warnings for missing required fields |
<JsonLd> Component
// Multiple separate <script> tags
<JsonLd schema={[productSchema, breadcrumbSchema]} />
// Single @graph block (Google recommended)
<JsonLd schema={[productSchema, breadcrumbSchema, orgSchema]} graph />Product Schema — Advanced Features
Reviews + Ratings
generateProductSchema({
name: "T-Shirt",
rating: 4.5,
reviewCount: 128,
reviews: [
{ author: "Alice", ratingValue: 5, reviewBody: "Amazing quality!", datePublished: "2024-01-15" },
{ author: "Bob", ratingValue: 4, reviewBody: "Good value", datePublished: "2024-02-01" },
],
});Return Policy
generateProductSchema({
returnPolicy: {
returnPolicyCategory: "MerchantReturnFiniteReturnWindow",
returnWithin: 30,
returnMethod: "ReturnByMail",
returnFees: "FreeReturn",
},
});Shipping
generateProductSchema({
shipping: {
shippingRate: { value: 5.99, currency: "USD" },
shippingDestination: "US",
deliveryTime: { minDays: 3, maxDays: 7 },
freeShippingThreshold: 50,
},
});Product Variants → AggregateOffer
generateProductSchema({
variants: [
{ name: "Small", sku: "TS-S", price: 19.99 },
{ name: "Medium", sku: "TS-M", price: 22.99 },
{ name: "Large", sku: "TS-L", price: 24.99 },
],
});
// → AggregateOffer { lowPrice: 19.99, highPrice: 24.99, offerCount: 3 }React / Pages Router
// _app.tsx
import { SEOProvider } from '@masters-ws/react-seo';
export default function App({ Component, pageProps }) {
return (
<SEOProvider config={{ name: "My Site", url: "https://mysite.com", description: "..." }}>
<Component {...pageProps} />
</SEOProvider>
);
}
// pages/products/[id].tsx
import { SeoProduct } from '@masters-ws/react-seo';
export default function ProductPage({ product }) {
return (
<>
<SeoProduct item={{
name: product.name,
description: product.description,
image: product.image,
price: product.price,
currency: "USD",
sku: product.sku,
}} />
<main>...</main>
</>
);
}⚠️ SSR vs CSR
// ✅ Correct — Server Component, Google sees everything
import { generateProductMetadata, JsonLd } from '@masters-ws/react-seo/core';
export async function generateMetadata() { /* ... */ }
export default function Page() {
return <JsonLd schema={schemas} graph />;
}
// ❌ Wrong — 'use client' means metadata loads after JS
'use client';
export default function Page() {
return <SEO title="..." />; // Google may miss this
}Generating SEO-Optimized Content
This package handles technical SEO perfectly. For generating the actual content that ranks — blog posts, product descriptions, landing pages — pair it with Outrank, the AI platform built specifically for SEO content at scale.
🚀 Get Started: outrank.so
🎁 Exclusive: Use code SHADI for 10% off your first month.
License
MIT © Shadi Shammaa
