SEO Plugin
Add SEO fields to your collections and globals without redefining them. The plugin extends each target with seoTitle and seoDescription, so editors can write optimized metadata per document.
Install
bun add @timbl/plugin-seoUse
Pass collections and globals arrays listing the keys where SEO fields should appear. The plugin uses extend to add fields to existing definitions without redefining them.
import { defineCMS, defineCollection } from "timbl";import { createSeoPlugin } from "@timbl/plugin-seo";
export default defineCMS({ collections: [ defineCollection({ key: "posts", labels: { singular: "Post", plural: "Posts" }, fields: [ { key: "title", type: "text", required: true }, { key: "slug", type: "slug", required: true }, { key: "body", type: "markdown" }, ], }), ], globals: [ { key: "siteSettings", label: "Site Settings", fields: [{ key: "siteName", type: "text" }] }, ], plugins: [ createSeoPlugin({ collections: ["posts"], globals: ["siteSettings"], }), ],});Added fields
Each targeted collection or global receives two fields:
| Key | Type | Purpose |
|---|---|---|
seoTitle | text | Short title for search results and social cards |
seoDescription | textarea | Summary for meta description and Open Graph |
Both fields are optional and appear after the existing fields on the document. They are standard field definitions, so they flow through the same validation, serialization, and API response pipeline as any other field.
Reading SEO values
SEO fields are returned like any other field in the API response. Read them from the document and render into your page head.
const post = await client.collection("posts").findBySlug("hello");const seoTitle = post.seoTitle ?? post.title;const seoDescription = post.seoDescription ?? post.body?.slice(0, 160);Options
type SeoPluginOptions = { collections?: string[]; // collection keys to extend globals?: string[]; // global keys to extend};Both arrays are optional. Omitting both installs the plugin without extending anything, which is rarely useful. Pass the keys you want extended.
Contract tier
The plugin definition shape and the createSeoPlugin factory are stable. The plugin is a thin definePlugin wrapper using extend, so it follows the same contract as any user-authored plugin. See Plugins for the extension model.
See also
- Plugins: the full plugin API
- Configuration: how plugins wire into
defineCMS - RSS Plugin: the other official field/route plugin