Build a programmatic SEO workflow
Define a page family, brief a writer or agent, inspect the result, and enforce the contract in CI.
This guide takes one page family from configuration to a repeatable publishing gate. By the end, you can brief a page before its Markdown file exists, give the same plan to a writer or agent, and catch structural or linking gaps before the page ships.
The working loop is:
- Connect Contentbit to the project.
- Define the page family.
- Plan one page.
- Generate its writing brief.
- Inspect the finished draft.
- Run the publishing gate.
Before you start
If Contentbit is not installed yet, scaffold the project with SEO support:
npx contentbit@latest init --seoThis creates contentbit.config.ts and a starter
contentbit.seo.config.ts. You can use the same steps below in an existing
installation by adding those files yourself.
1. Connect the project
Put the shared command settings in contentbit.config.ts:
import { defineContentConfig } from '@contentbit/core'
export default defineContentConfig({
content: 'content/**/*.md',
registry: './blocks/registry.ts',
seo: './contentbit.seo.config.ts',
})Contentbit now knows which files to scan, which block registry to load, and where the SEO contracts live. The commands in the rest of this guide do not need repeated glob, registry, or SEO config flags.
Run a first scan to confirm the project loads:
contentbit doctorIt is normal to see SEO findings at this point. The next steps define what the pages should satisfy.
2. Define the page family
A page type describes the structure shared by every page in a family. This example defines glossary pages:
// contentbit.seo.config.ts
import { defineSeoConfig } from '@contentbit/core'
export default defineSeoConfig({
pageTypes: {
glossary: {
requiredFrontmatter: ['name', 'summary', 'seoKeywords.primary'],
requiredSections: [
{ id: 'definition', headings: ['What it means', 'Definition'] },
{ id: 'why-it-matters', headings: ['Why it matters'] },
],
recommendedBlocks: ['quick-ref', 'faq'],
minOutgoingLinks: 2,
},
},
pageDefaults: [
{
pathPrefix: 'content/glossary/',
type: 'glossary',
intent: 'definition',
},
],
})pageDefaults applies the glossary contract to existing files in that folder.
Use explicit page plans when a page does not exist yet or needs its own target
keywords and links.
For every available contract field, see SEO briefs and contracts.
3. Plan one page
Add the first planned page to the same SEO config:
// Add this beside pageTypes and pageDefaults.
pages: {
'content/glossary/recipe-import/en.md': {
type: 'glossary',
key: 'glossary/recipe-import',
slug: 'recipe-import',
title: 'Recipe import',
intent: 'definition',
keywords: {
primary: 'recipe import',
secondary: ['recipe importer', 'save recipes from websites'],
},
linksTo: ['glossary/recipe-manager', 'guides/save-recipes-from-websites'],
},
},The file does not need to exist yet. Contentbit can now resolve the page by its stable key or slug and combine its target facts with the glossary contract.
Check that the plan resolves:
contentbit brief glossary/recipe-importYou should see the page target, required sections, recommended blocks, required links, related pages, and acceptance checks.
4. Brief the writer
Use the Markdown brief for a human or an editorial ticket:
contentbit brief glossary/recipe-importUse JSON when a coding agent or script will consume it:
contentbit brief glossary/recipe-import --jsonGive the brief to the writer as the contract for this page. A short agent instruction is enough:
Write the planned glossary/recipe-import page from this Contentbit brief.
Preserve the target facts, include every required section, satisfy the required
links, and run contentbit doctor before you finish.The brief defines the page structure. The live registry explains the syntax for any structured blocks:
contentbit instructions --audience llmThis separation keeps the page plan focused while ensuring the agent never has to guess block names, props, or body formats.
5. Inspect the draft
Once the writer creates the Markdown file, open Studio:
contentbit studioOpen the page and check four views:
- Preview for the rendered page.
- Brief for the original target and acceptance checks.
- Findings for structural, block, and SEO issues.
- Links for outgoing links and backlinks.
The live frontmatter becomes the source for page facts such as title, slug,
intent, keywords, and linksTo. The page plan continues to supply anything
the file does not override.
6. Run the publishing gate
Run Doctor after the draft exists:
contentbit doctorThe report combines normal content health checks with the SEO contract. It can flag:
- missing required frontmatter
- missing required sections
- missing required or recommended blocks
- missing required links
- too few outgoing links or backlinks
- invalid block markup, thin sections, and missing image alt text
Fix required findings, then run the strict gate:
contentbit doctor --strict-seoThe command exits with a failure when required SEO findings remain, so the same check works for a local handoff, an agent repair loop, and CI.
7. Add the gate to CI
Keep the package scripts short because contentbit.config.ts already carries
the project settings:
{
"scripts": {
"content:doctor": "contentbit doctor --strict-seo",
"studio": "contentbit studio"
}
}Run content:doctor in the same CI job that builds the site. A page that no
longer satisfies its family contract will fail before publishing.
Multilingual page families
For translated slugs, configure one link strategy for every command:
// contentbit.config.ts
export default defineContentConfig({
content: 'content/**/*.md',
registry: './blocks/registry.ts',
seo: './contentbit.seo.config.ts',
links: {
resolve: 'same-locale-key',
localeField: 'locale',
keyField: 'key',
slugField: 'slug',
},
})Translated pages share a stable key and keep their own locale-specific slug:
---
key: glossary/recipe-import
locale: fr
slug: import-recettes
linksTo:
- glossary/recipe-manager
seoKeywords:
primary: import de recettes
---Contentbit keeps each localized SEO page separate while resolving links within
the right locale. seoKeywords is normalized as keyword data when keywords
is absent.
If something does not resolve
- No SEO config found: confirm that
contentbit.config.tspoints tocontentbit.seo.config.ts. - Brief target not found: try the page's stable
key, then itsslug. - Page has no type: add a matching
pageDefaultsprefix or settypein an explicit page plan. - Links resolve in the wrong locale: put the shared resolver under
linksincontentbit.config.tsso every command uses the same strategy.
What to read next
- SEO briefs and contracts for the full config reference.
- Content Studio for the local review interface.
- Content doctor for findings and exit behavior.
- Internal linking for aliases, backlinks, and resolver modes.
- LLM-agent integration for project-local agent instructions.