smart-seo-engine
v1.0.0
Published
Production-ready, config-driven technical SEO automation for React and Next.js. Deterministic metadata, JSON-LD schemas, sitemap/robots generators, and SEO audit tools.
Maintainers
Readme
smart-seo-engine
Production-ready, config-driven technical SEO automation for React and Next.js.
Deterministic metadata, JSON-LD schemas, sitemap/robots generators, and SEO audit tools.
No AI. No LLMs. Pure config-driven logic.
Features
- 🧩 React Component — Drop-in
<SEO />component for any React app - ⚡ Next.js Metadata —
buildMetadata()for App RoutergenerateMetadata - 📊 JSON-LD Schemas — 10+ schema builders (Organization, Article, Product, FAQ, etc.)
- 🗺️ Sitemap Generator — XML sitemap with hreflang support
- 🤖 robots.txt Generator — Config-driven robots.txt generation
- ✅ SEO Audit — Automated scoring, grading, and recommendations
- 🌍 Multilingual — hreflang, alternates, locale support
- 🔧 Config-Driven — One config, all pages. Overridable per page.
- 📦 Tree-Shakable — Three entry points: core, React, Next.js
- 🛡️ TypeScript First — Full type safety with
.d.tsexports - 🟢 Zero Runtime Deps — No external dependencies
Installation
npm install smart-seo-engineyarn add smart-seo-enginepnpm add smart-seo-engineQuick Start
1. Create SEO Config
import { createSEOConfig } from 'smart-seo-engine';
const seoConfig = createSEOConfig({
siteName: 'My Site',
siteUrl: 'https://example.com',
defaultTitle: 'My Site',
titleTemplate: '%s | My Site',
defaultDescription: 'Default site description',
defaultImage: '/og-default.png',
locale: 'en',
locales: ['en', 'ar'],
twitterHandle: '@mysite',
companyName: 'My Company',
defaultRobots: { index: true, follow: true },
});2. Use in React
import { SEO, createSEOConfig } from 'smart-seo-engine/react';
function AboutPage() {
return (
<>
<SEO
config={seoConfig}
title="About Us"
description="Learn more about our company"
path="/about"
type="webpage"
/>
<main>
<h1>About Us</h1>
</main>
</>
);
}3. Use in Next.js
// app/about/page.tsx
import { buildMetadata } from 'smart-seo-engine/next';
export const metadata = buildMetadata(seoConfig, {
title: 'About Us',
description: 'Learn more about our company',
path: '/about',
type: 'webpage',
});
export default function AboutPage() {
return <main><h1>About Us</h1></main>;
}Entry Points
| Import Path | Description | React Required |
|---|---|---|
| smart-seo-engine | Core utilities, schemas, generators, audit | ❌ |
| smart-seo-engine/react | React <SEO /> component and hooks | ✅ |
| smart-seo-engine/next | Next.js metadata builders | ✅ |
Config API
const config = createSEOConfig({
// Required
siteName: 'My Site',
siteUrl: 'https://example.com',
defaultTitle: 'My Site',
// Optional
titleTemplate: '%s | My Site', // %s = page title
defaultDescription: 'Site desc',
defaultImage: '/og.png',
defaultImageAlt: 'Site preview',
locale: 'en', // Primary locale
locales: ['en', 'ar', 'fr'], // All supported locales
twitterHandle: '@handle',
twitterCardType: 'summary_large_image',
companyName: 'Company Inc.',
defaultAuthor: 'Author Name',
includeWebSiteSchema: true, // Auto-include WebSite schema
defaultRobots: {
index: true,
follow: true,
maxSnippet: -1,
maxImagePreview: 'large',
},
urlNormalization: {
trailingSlash: false,
forceLowercase: true,
},
organization: {
name: 'Company Inc.',
url: 'https://example.com',
logo: 'https://example.com/logo.png',
sameAs: ['https://twitter.com/company'],
},
verification: {
google: 'google-verification-code',
bing: 'bing-verification-code',
},
});Page-Level SEO Props
All page-level properties can be passed to <SEO />, buildMetadata(), or resolveSEO():
{
title: 'Page Title',
description: 'Page description',
path: '/page-path',
canonical: 'https://example.com/page', // Override canonical
image: '/page-og.png',
imageAlt: 'Page image description',
imageWidth: 1200,
imageHeight: 630,
keywords: ['keyword1', 'keyword2'],
author: 'Author Name',
type: 'article', // webpage | article | product | service | faq | project | ...
locale: 'en',
noindex: false,
nofollow: false,
publishedTime: '2026-01-01T00:00:00Z',
modifiedTime: '2026-04-01T00:00:00Z',
skipTitleTemplate: false,
ogType: 'article',
alternates: [
{ hreflang: 'ar', href: 'https://example.com/ar/page' },
],
breadcrumbs: [
{ name: 'Home', url: 'https://example.com' },
{ name: 'Page', url: 'https://example.com/page' },
],
faq: [
{ question: 'What?', answer: 'Something.' },
],
product: { name: 'Widget', price: 29.99, ... },
service: { name: 'Dev', serviceType: 'Web', ... },
article: { section: 'Tech', tags: ['react'] },
organization: { name: 'Org', ... },
localBusiness: { name: 'Shop', ... },
creativeWork: { name: 'Project', ... },
pagination: { prev: '/page?p=1', next: '/page?p=3' },
customSchemas: [{ '@type': 'Event', name: '...' }],
}React Usage
<SEO /> Component
import { SEO, createSEOConfig } from 'smart-seo-engine/react';
<SEO
config={seoConfig}
title="Page Title"
description="Page description"
path="/page"
type="webpage"
/>useSEO Hook
import { useSEO, createSEOConfig } from 'smart-seo-engine/react';
function MyPage() {
const seo = useSEO(seoConfig, {
title: 'My Page',
path: '/my-page',
});
// Access resolved data: seo.title, seo.metaTags, seo.linkTags, seo.schemas
return <h1>{seo.title}</h1>;
}<JsonLd /> Component
import { JsonLd } from 'smart-seo-engine/react';
import { buildOrganizationSchema } from 'smart-seo-engine';
<JsonLd schema={buildOrganizationSchema({ name: 'My Org', url: '...' })} />Next.js Usage
Static Metadata
// app/about/page.tsx
import { buildMetadata } from 'smart-seo-engine/next';
export const metadata = buildMetadata(seoConfig, {
title: 'About',
description: 'About page',
path: '/about',
});Dynamic Metadata
// app/blog/[slug]/page.tsx
import { buildMetadata } from 'smart-seo-engine/next';
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
return buildMetadata(seoConfig, {
title: post.title,
description: post.excerpt,
path: `/blog/${post.slug}`,
type: 'article',
publishedTime: post.publishedAt,
});
}Viewport
import { buildViewport } from 'smart-seo-engine/next';
export const viewport = buildViewport({
themeColor: '#ffffff',
colorScheme: 'light dark',
});JSON-LD (Server Component)
import { SeoJsonLd, buildSchemaGraph, buildWebSiteSchema } from 'smart-seo-engine/next';
export default function Layout({ children }) {
const schema = buildSchemaGraph([buildWebSiteSchema(seoConfig)]);
return (
<html>
<body>
<SeoJsonLd schema={schema} />
{children}
</body>
</html>
);
}Sitemap Generation
import { generateSitemap } from 'smart-seo-engine';
const xml = generateSitemap({
siteUrl: 'https://example.com',
routes: [
{ path: '/', lastModified: '2026-04-14', changeFrequency: 'weekly', priority: 1.0 },
{ path: '/about', lastModified: '2026-04-10', changeFrequency: 'monthly', priority: 0.8 },
{
path: '/blog',
alternates: [
{ hreflang: 'en', href: 'https://example.com/blog' },
{ hreflang: 'ar', href: 'https://example.com/ar/blog' },
],
},
],
defaultChangeFrequency: 'monthly',
defaultPriority: 0.5,
});
// In Next.js: app/sitemap.xml/route.ts
export function GET() {
return new Response(xml, { headers: { 'Content-Type': 'application/xml' } });
}Sitemap Index (for large sites)
import { generateSitemapIndex } from 'smart-seo-engine';
const index = generateSitemapIndex('https://example.com', [
'/sitemap-pages.xml',
'/sitemap-blog.xml',
'/sitemap-products.xml',
]);robots.txt Generation
import { generateRobotsTxt } from 'smart-seo-engine';
const robotsTxt = generateRobotsTxt({
siteUrl: 'https://example.com',
rules: [
{
userAgent: '*',
allow: ['/'],
disallow: ['/admin', '/api', '/private'],
},
{
userAgent: 'Googlebot',
allow: ['/'],
crawlDelay: 2,
},
],
sitemaps: ['/sitemap.xml'],
host: 'example.com',
});
// In Next.js: app/robots.txt/route.ts
export function GET() {
return new Response(robotsTxt, { headers: { 'Content-Type': 'text/plain' } });
}SEO Audit
import { reportSEO, formatReport } from 'smart-seo-engine';
const report = reportSEO({
url: 'https://example.com/about',
title: 'About Us | My Site',
description: 'Learn more about our company and what we do.',
canonical: 'https://example.com/about',
image: 'https://example.com/og-about.png',
hasStructuredData: true,
hasH1: true,
ogTitle: 'About Us | My Site',
ogDescription: 'Learn more about our company.',
locales: ['en', 'ar'],
alternates: [
{ hreflang: 'en', href: 'https://example.com/about' },
{ hreflang: 'ar', href: 'https://example.com/ar/about' },
],
breadcrumbs: [
{ name: 'Home', url: 'https://example.com' },
{ name: 'About', url: 'https://example.com/about' },
],
hasBreadcrumbSchema: true,
});
console.log(report.score); // 95
console.log(report.grade); // "A"
console.log(report.errors); // []
console.log(report.warnings); // [...]
console.log(report.passed); // [...]
console.log(report.recommendations); // [...]
// Pretty print
console.log(formatReport(report));Audit Checks
| Check | Severity | Description |
|---|---|---|
| title-missing | Error | Page has no title |
| title-too-short | Warning | Title < 30 characters |
| title-too-long | Warning | Title > 60 characters |
| description-missing | Error | No meta description |
| description-too-short | Warning | Description < 50 chars |
| description-too-long | Warning | Description > 160 chars |
| canonical-missing | Error | No canonical URL |
| og-image-missing | Warning | No OG image |
| structured-data-missing | Warning | No JSON-LD |
| h1-missing | Warning | No H1 heading |
| alternates-missing | Warning | Missing hreflang links |
| breadcrumb-schema-missing | Warning | Breadcrumbs without schema |
| og-title-missing | Warning | Missing og:title |
| og-description-missing | Warning | Missing og:description |
| duplicate-url | Warning | Duplicate URL in route map |
| noindex-detected | Info | Page set to noindex |
Supported Schemas
import {
buildOrganizationSchema,
buildWebSiteSchema,
buildWebPageSchema,
buildArticleSchema,
buildProductSchema,
buildServiceSchema,
buildFAQSchema,
buildBreadcrumbSchema,
buildLocalBusinessSchema,
buildCreativeWorkSchema,
buildSchemaGraph,
} from 'smart-seo-engine';| Schema | Builder | Auto-Included |
|---|---|---|
| Organization | buildOrganizationSchema() | When org data exists |
| WebSite | buildWebSiteSchema() | Always (configurable) |
| WebPage | buildWebPageSchema() | Always |
| Article | buildArticleSchema() | When type: 'article' |
| Product | buildProductSchema() | When type: 'product' |
| Service | buildServiceSchema() | When type: 'service' |
| FAQPage | buildFAQSchema() | When type: 'faq' or FAQ data |
| BreadcrumbList | buildBreadcrumbSchema() | When breadcrumbs data |
| LocalBusiness | buildLocalBusinessSchema() | When type: 'local-business' |
| CreativeWork | buildCreativeWorkSchema() | When type: 'project' |
Schema Graph
Combine multiple schemas into a single @graph:
const graph = buildSchemaGraph([
buildOrganizationSchema({ name: 'My Org', url: '...' }),
buildWebSiteSchema(config),
buildBreadcrumbSchema([
{ name: 'Home', url: '/' },
{ name: 'About', url: '/about' },
]),
]);
// Output: { "@context": "https://schema.org", "@graph": [...] }Supported Page Types
| Type | OG Type | Auto Schemas |
|---|---|---|
| website | website | WebSite, WebPage |
| webpage | website | WebPage |
| article | article | WebPage, Article |
| blog | article | WebPage, Article |
| service | website | WebPage, Service |
| product | product | WebPage, Product |
| project | website | WebPage, CreativeWork |
| faq | website | WebPage, FAQPage |
| profile | profile | WebPage |
| organization | website | WebPage, Organization |
| local-business | business | WebPage, LocalBusiness |
Extending
Custom Schema
<SEO
config={seoConfig}
title="Event Page"
path="/events/conference-2026"
customSchemas={[
{
'@context': 'https://schema.org',
'@type': 'Event',
name: 'Tech Conference 2026',
startDate: '2026-09-15',
location: { '@type': 'Place', name: 'Convention Center' },
},
]}
/>Direct Schema Usage
import { buildOrganizationSchema, serializeSchema } from 'smart-seo-engine';
const schema = buildOrganizationSchema({
name: 'Company',
url: 'https://example.com',
logo: 'https://example.com/logo.png',
sameAs: ['https://twitter.com/company'],
address: {
streetAddress: '123 Main St',
addressLocality: 'Springfield',
addressCountry: 'US',
},
});
// Serialize for manual injection
const json = serializeSchema(schema);URL Utilities
import { normalizeUrl, joinUrl, resolveCanonical, createAlternateLinks } from 'smart-seo-engine';
normalizeUrl('https://example.com/About/', { trailingSlash: false, forceLowercase: true });
// → 'https://example.com/about'
joinUrl('https://example.com', '/about');
// → 'https://example.com/about'
createAlternateLinks('https://example.com', { en: '/about', ar: '/ar/about' });
// → [{ hreflang: 'en', href: '...' }, { hreflang: 'ar', href: '...' }]TypeScript Support
Full type definitions are included and exports are strongly typed:
import type {
SEOConfig,
SEOPageInput,
ResolvedSEO,
MetaTag,
LinkTag,
PageType,
AuditReport,
SitemapConfig,
RobotsTxtConfig,
OrganizationData,
ProductData,
ArticleData,
// ... and more
} from 'smart-seo-engine';JavaScript Support
Works identically for JavaScript consumers:
const { createSEOConfig, generateSitemap, reportSEO } = require('smart-seo-engine');
const config = createSEOConfig({
siteName: 'My Site',
siteUrl: 'https://example.com',
defaultTitle: 'My Site',
});API Reference
Core
| Function | Description |
|---|---|
| createSEOConfig(input) | Create a validated, frozen SEO config |
| resolveSEO(config, input) | Resolve all SEO data (title, meta, links, schemas) |
| validateSEO(config, input) | Validate SEO input and return issues |
| isValidSEO(config, input) | Returns true if no critical errors |
Builders
| Function | Description |
|---|---|
| buildTitle(config, input) | Resolve page title with template |
| buildCanonical(config, input) | Resolve canonical URL |
| buildMetaTags(config, input) | Build core meta tags |
| buildOpenGraphTags(config, input) | Build OG meta tags |
| buildTwitterTags(config, input) | Build Twitter Card tags |
| buildRobotsTags(config, input) | Build robots meta tags |
| buildAlternateTags(config, input) | Build hreflang link tags |
| createAlternateLinks(siteUrl, map) | Helper for locale-path maps |
Schemas
| Function | Description |
|---|---|
| buildSchemasForPage(config, input) | Auto-build all relevant schemas |
| buildSchemaGraph(schemas) | Combine schemas into @graph |
| buildOrganizationSchema(data) | Organization JSON-LD |
| buildWebSiteSchema(config) | WebSite JSON-LD |
| buildWebPageSchema(config, input) | WebPage JSON-LD |
| buildArticleSchema(config, input) | Article JSON-LD |
| buildProductSchema(data) | Product JSON-LD |
| buildServiceSchema(data) | Service JSON-LD |
| buildFAQSchema(items) | FAQPage JSON-LD |
| buildBreadcrumbSchema(items) | BreadcrumbList JSON-LD |
| buildLocalBusinessSchema(data) | LocalBusiness JSON-LD |
| buildCreativeWorkSchema(data) | CreativeWork JSON-LD |
Generators
| Function | Description |
|---|---|
| generateSitemap(config) | Generate XML sitemap string |
| generateSitemapIndex(siteUrl, paths) | Generate sitemap index XML |
| generateRobotsTxt(config) | Generate robots.txt string |
Audit
| Function | Description |
|---|---|
| auditSEO(input) | Run audit, return findings |
| scoreSEO(findings) | Calculate score (0-100) |
| scoreToGrade(score) | Convert score to letter grade |
| reportSEO(input) | Full audit report with score |
| formatReport(report) | Format report for display |
React
| Export | Description |
|---|---|
| <SEO /> | All-in-one SEO component |
| <JsonLd /> | JSON-LD script injector |
| useSEO(config, input) | Hook for resolved SEO data |
Next.js
| Export | Description |
|---|---|
| buildMetadata(config, input) | Build Next.js Metadata object |
| buildViewport(options) | Build Next.js Viewport object |
| buildAlternates(config, input) | Build alternates for metadata |
| <SeoJsonLd /> | Server Component JSON-LD |
License
MIT © abanobwagih
