Concepts
The SEO Object
The SEO type is the single source of truth after createSEO:
SEO = {
meta: {
title: string // required after normalize
description?: string
canonical?: string
robots?: string
alternates?: { languages?: Record<string,string>, canonical?: string }
pagination?: { prev?: string, next?: string }
verification?: { google?: string, bing?: string, ... }
}
openGraph?: { title?, description?, images[], type?, url?, siteName?, locale? }
twitter?: { card?, title?, description?, image?, site?, creator? }
schema?: JSONLD[] // structured data nodes
}
The Pipeline
Partial<SEO> + SEOConfig
→ applyRules (optional, needs route string)
→ createSEO (normalize + fallbacks)
→ run plugins (beforeMerge / afterMerge)
→ SEO (canonical document)
→ Adapter.render(seo) → framework metadata | Helmet props | TagDescriptor[]
Merge Strategy
mergeSEO(parent, child, config?):
| Field | Strategy |
|---|---|
meta.title |
Child overrides |
meta.description |
Child overrides |
meta.alternates.languages |
Deep merge (incl. x-default) |
openGraph.images |
Replace array (child wins) |
schema |
Concatenate; optional dedupeByIdAndType |
| All other scalars | Child overrides |
JSON-LD Serialization
All structured data goes through serializeJSONLD — never
ad-hoc string concat. This prevents </script> injection and
U+2028/U+2029 breaks.
import { serializeJSONLD, article } from "@better-seo/core"
// Safe for embedding in <script type="application/ld+json">
const json = serializeJSONLD(article({ headline: title, url }))
Config & Context
SEOConfig controls defaults:
type SEOConfig = {
defaultTitle: string
description?: string
baseUrl?: string
titleTemplate?: string // "%s | Site Name"
defaultRobots?: string
schemaMerge?: "concat" | "dedupeByIdAndType"
rules?: SEORule[]
plugins?: SEOPlugin[]
}
For Edge/Workers/multi-tenant: use createSEOContext(explicitConfig) per
request — no filesystem reads.
Adapter Contract
Every adapter maps SEO → framework output:
interface SEOAdapter<TOutput> {
id: string
toFramework(seo: SEO): TOutput
}
- Next.js:
toNextMetadata(seo): Metadata - React:
toHelmetProps(seo): HelmetProps - Vanilla:
renderTags(seo): TagDescriptor[]
Next
- API Reference — full symbol documentation
- Recipes — practical patterns