@farmtrace/seo
v1.0.3
Published
Comprehensive SEO toolkit for Next.js applications with programmatic SEO, JSON-LD schema, and content generation support
Downloads
35
Readme
@farmtrace/seo
A comprehensive, plugin-based SEO toolkit for React applications. Handles metadata generation, JSON-LD structured data, sitemaps, breadcrumbs, and programmatic SEO — all configured with a single createSEO() call.
Works with any React framework: Next.js, Remix, Vite, Gatsby, Astro, or plain React.
Features
- Framework-agnostic — works with Next.js, Remix, Vite, Gatsby, Astro, or plain React
- Zero-config defaults — only
nameanddomainrequired, everything else has smart defaults - Plugin architecture — extend with custom schemas, templates, page types, and middleware
- JSON-LD schema — Organization, WebSite, Article, FAQ, BreadcrumbList, SoftwareApplication, HowTo, Person
- Metadata API — generates structured metadata objects compatible with Next.js
Metadataand any other framework - Page registry — register pages for automatic sitemap generation and breadcrumbs
- AutoSEO component — React component that auto-injects JSON-LD from conventions
- Programmatic SEO — templates, content hashing, and uniqueness scoring for generated pages
- March 2026 Core Update compliant — E-E-A-T Person schema, Information Gain scoring, content uniqueness validation
- Tree-shakeable — subpath imports for each module
Installation
# npm
npm install @farmtrace/seo
# bun
bun add @farmtrace/seo
# pnpm
pnpm add @farmtrace/seoPeer Dependencies
{
"react": ">=18"
}Next.js is optional — the package works without it.
Quick Start
1. Initialize
Call createSEO() once in your app (e.g., in lib/seo/config.ts):
import { createSEO } from '@farmtrace/seo';
createSEO({
name: 'MyApp',
domain: 'myapp.com',
tagline: 'Build better apps',
social: {
twitter: '@myapp',
linkedin: 'https://linkedin.com/company/myapp',
},
images: {
logo: '/logo.png',
ogDefault: '/og-image.jpg',
},
});2. Generate Metadata
// Works with any framework — returns a plain Metadata object
import { generateMetadata as genMeta } from '@farmtrace/seo';
import type { Metadata } from '@farmtrace/seo';
function getPageMetadata(): Metadata {
return genMeta({
title: 'About Us',
description: 'Learn about our mission and team.',
path: '/about',
type: 'generic',
});
}3. Add JSON-LD Schema
import { AutoSEO } from '@farmtrace/seo';
export default function AboutPage() {
return (
<AutoSEO path="/about" schemaOptions={{ name: 'About Us', description: '...' }}>
<main>{/* page content */}</main>
</AutoSEO>
);
}API Reference
Core
| Export | Description |
|--------|-------------|
| createSEO(options) | Initialize the SEO system. Returns an SEOInstance with runtime extension methods. |
| configureSEO(config) | Legacy configuration (use createSEO instead). |
| definePlugin(plugin) | Type-safe plugin definition helper. |
| getSiteConfig() | Get the resolved SiteConfig. |
| getTitleTemplates() | Get all title templates as Record<string, string>. |
| getDescriptionTemplates() | Get all description templates as Record<string, string>. |
| getKeywords() | Get the keyword dictionary. |
| addTemplate(kind, key, template) | Register a title or description template at runtime. |
| resolveTemplate(kind, key, vars) | Interpolate a registered template with variables. |
| addSchema(type, generator) | Register a custom schema generator. |
| addMiddleware(fn) | Add metadata middleware. |
| addPageType(type, config) | Register a custom page type. |
Metadata
| Export | Description |
|--------|-------------|
| generateMetadata(options) | Generate a Next.js Metadata object. |
| generateProgrammaticMetadata(options) | Metadata for programmatic/template pages. |
| generateBlogPostMetadata(options) | Specialized blog post metadata. |
| generateIntegrationMetadata(options) | Specialized integration page metadata. |
| generateFeatureMetadata(options) | Specialized feature page metadata. |
| generateUseCaseMetadata(options) | Specialized use-case page metadata. |
| generateLocationMetadata(options) | Specialized location page metadata. |
| mergeMetadata(...sources) | Deep-merge multiple metadata objects. |
| createMetadataGenerator(defaults) | Create a reusable metadata generator with defaults. |
| formatTitle(title) | Apply the default title template. |
| stripHtml(str) | Remove HTML tags from a string. |
| truncateText(text, max) | Truncate text to a max length with ellipsis. |
Schema (JSON-LD)
| Export | Description |
|--------|-------------|
| generateOrganizationSchema() | Organization schema from site config. |
| generateWebSiteSchema() | WebSite schema with search action. |
| generateWebPageSchema(path, options) | WebPage (or subtype) schema. |
| generateArticleSchema(path, options) | Article schema with author and dates. |
| generatePersonSchema(person) | Person schema with E-E-A-T signals. |
| generateBreadcrumbSchema(items) | BreadcrumbList schema from items. |
| generateBreadcrumbsFromPath(path) | Auto-generate breadcrumbs from a URL path. |
| generateFAQSchema(items) | FAQPage schema. |
| generateSoftwareSchema(options) | SoftwareApplication schema. |
| generateHowToSchema(options) | HowTo schema with steps. |
| wrapSchema(schema) | Wrap a schema with @context. |
| generateSchemaGraph(schemas) | Combine schemas into a @graph. |
| schemaToJsonString(schema) | Serialize schema to JSON string. |
| isValidSchema(schema) | Validate a schema has required @type. |
Page Registry
| Export | Description |
|--------|-------------|
| registerStaticPages(pages) | Register known pages. |
| registerDynamicPages(generator) | Register a dynamic page generator. |
| getStaticPages() | Get all static pages. |
| getDynamicPages() | Resolve and get all dynamic pages. |
| getAllPages() | Get all pages (static + dynamic). |
| findPageByPath(path) | Look up a page by its path. |
| getPageConfig(path, overrides?) | Get config or auto-generate from conventions. |
| generatePageConfigFromPath(path) | Generate page config from URL conventions. |
| generateSitemapFromRegistry(baseUrl) | Generate sitemap entries from all registered pages. |
| generateBreadcrumbsFromRegistry(path) | Generate breadcrumb trail from registry. |
| getRelatedPages(path, limit?) | Find related pages by relevance scoring. |
Auto-SEO
| Export | Description |
|--------|-------------|
| AutoSEO | React component — auto-injects JSON-LD schema markup. |
| createPageMetadata(path, options?) | Convention-based metadata generation. |
| generateAutoSchema(path, options?) | Convention-based schema generation. |
| createFullPageSEO(path, options) | Returns both { metadata, schema } in one call. |
Constants
| Constant | Description |
|----------|-------------|
| PAGE_TYPES | Maps page types → Schema.org types (home → WebSite, article → Article, etc.) |
| SEO_LIMITS | Title/description character limits ({ min, max, optimal }) |
| REVALIDATION | ISR revalidation intervals by content type (seconds) |
| SITEMAP_CONFIG | Sitemap defaults: max URLs, priorities, change frequencies |
| CONTENT_REQUIREMENTS | Minimum word counts, required sections, unique content ratio |
Utilities
| Export | Description |
|--------|-------------|
| getCanonicalUrl(path) | Build canonical URL from path and site config. |
| getAlternateUrls(path) | Generate alternate language URLs. |
| validateTitle(title) | Check title against SEO_LIMITS. |
| validateDescription(desc) | Check description against SEO_LIMITS. |
| generateContentHash(content) | Generate a hash for content uniqueness tracking. |
Subpath Imports
Import only the module you need to keep bundles minimal:
import { createSEO, definePlugin } from '@farmtrace/seo/core';
import { generateArticleSchema } from '@farmtrace/seo/schema';
import { generateMetadata } from '@farmtrace/seo/metadata';
import { registerStaticPages } from '@farmtrace/seo/registry';
import { AutoSEO } from '@farmtrace/seo/auto-seo';
import type { SEOPlugin } from '@farmtrace/seo/types';
import { PAGE_TYPES } from '@farmtrace/seo/constants';
import { getCanonicalUrl } from '@farmtrace/seo/utils';Plugin System
Plugins bundle related functionality — schemas, templates, page types, and middleware:
import { definePlugin, createSEO } from '@farmtrace/seo';
const analyticsPlugin = definePlugin({
name: 'analytics',
middleware: [
(metadata, ctx) => {
// Add tracking parameters to all pages
metadata['other'] = { ...metadata['other'], 'analytics-page-type': ctx.type };
return metadata;
},
],
});
const ecommercePlugin = definePlugin({
name: 'ecommerce',
schemas: {
product: (data) => ({
'@type': 'Product',
name: data?.name,
description: data?.description,
offers: { '@type': 'Offer', price: data?.price, priceCurrency: 'USD' },
}),
},
pageTypes: {
product: { schemaType: 'Product', ogType: 'product', priority: 0.8 },
collection: { schemaType: 'CollectionPage', ogType: 'website', priority: 0.7 },
},
templates: {
title: { product: '%s | Shop', collection: '%s Collection | Shop' },
description: { product: 'Buy %s at great prices.' },
},
setup: (seo) => {
console.log(`Ecommerce plugin loaded for ${seo.config.site.name}`);
},
});
createSEO({
name: 'MyShop',
domain: 'myshop.com',
plugins: [analyticsPlugin, ecommercePlugin],
});Page Registry & Sitemap
import {
registerStaticPages,
registerDynamicPages,
generateSitemapFromRegistry,
} from '@farmtrace/seo';
// Register your pages
registerStaticPages([
{ path: '/', title: 'Home', description: 'Welcome', type: 'home', priority: 1.0 },
{ path: '/about', title: 'About', description: 'About us', type: 'about', parent: '/' },
{ path: '/blog', title: 'Blog', description: 'Latest posts', type: 'blog-listing' },
]);
registerDynamicPages({
pattern: '/blog/[slug]',
type: 'blog-post',
generate: async () => {
const posts = await fetchPosts();
return posts.map(p => ({
path: `/blog/${p.slug}`,
title: p.title,
description: p.excerpt,
type: 'blog-post' as const,
}));
},
});
// In your sitemap.ts
export default async function sitemap() {
return generateSitemapFromRegistry('https://myshop.com');
}Architecture
src/
├── index.ts # Public API (barrel exports)
├── types.ts # All type definitions
├── constants.ts # PAGE_TYPES, SEO_LIMITS, REVALIDATION, etc.
├── utils.ts # URL helpers, validators, hashing
├── core/
│ ├── state.ts # Singleton config store
│ ├── factory.ts # createSEO() + configureSEO()
│ ├── getters.ts # Config accessor functions
│ ├── templates.ts # Template registration + interpolation
│ ├── plugins.ts # definePlugin() + applyPlugin()
│ └── extensions.ts # Runtime schema/middleware/pageType registration
├── schema/
│ ├── types.ts # Schema.org TypeScript interfaces
│ ├── generators.ts # All generate*Schema() functions
│ └── utils.ts # wrapSchema, generateSchemaGraph, etc.
├── metadata/
│ ├── generator.ts # generateMetadata, generateProgrammaticMetadata
│ ├── specialized.ts # Blog, integration, feature, use-case, location generators
│ └── utils.ts # mergeMetadata, formatTitle, stripHtml, truncateText
├── registry/
│ ├── store.ts # Page registration + lookup
│ ├── conventions.ts # Path-based page config inference
│ └── sitemap.ts # Sitemap + breadcrumbs + related pages
└── auto-seo/
├── metadata.ts # createPageMetadata (convention-based)
├── schema.ts # generateAutoSchema (convention-based)
└── component.tsx # <AutoSEO> React component + createFullPageSEORequirements
- React ≥ 18
- TypeScript ≥ 5 (recommended)
- Next.js ≥ 14 (optional — for
generateMetadataintegration)
License
MIT
