:::contentbit
How-to guides

SEO briefs and contracts

Define page contracts and print agent-ready briefs for existing or planned pages.

SEO contracts describe what a page must satisfy. Briefs turn those contracts plus the current content inventory into a writer-ready plan.

Use this page as a reference while editing contentbit.seo.config.ts. To build the workflow from start to finish, follow Build a programmatic SEO workflow.

How the config files fit together

The project config tells Contentbit where the SEO config lives:

// contentbit.config.ts
import { defineContentConfig } from '@contentbit/core'

export default defineContentConfig({
  content: 'content/**/*.md',
  registry: './blocks/registry.ts',
  seo: './contentbit.seo.config.ts',
})

The SEO config holds page-family contracts and individual page plans. Keeping the two concerns separate lets every CLI command share the same project paths without mixing them into the editorial model.

Config file

Create contentbit.seo.config.ts at the project root, or point to another path from contentbit.config.ts. Keep the file small at first. Define one page type, add folder defaults, then add explicit plans where a writer needs a brief before the Markdown file exists.

import { defineSeoConfig } from '@contentbit/core'

export default defineSeoConfig({
  pageTypes: {
    alternative: {
      requiredFrontmatter: ['type', 'intent', 'keywords.primary'],
      requiredSections: [
        { id: 'overview', headings: ['Overview', 'Summary'] },
        { id: 'alternatives', headings: ['Best alternatives', 'Top alternatives'] },
        { id: 'comparison', headings: ['Comparison', 'Feature comparison'] },
      ],
      requiredBlocks: ['comparison'],
      recommendedBlocks: ['faq', 'key-metrics'],
      requiredLinksTo: ['seo-tools-comparison'],
      minOutgoingLinks: 2,
      minIncomingLinks: 1,
    },
  },
  pageDefaults: [
    {
      pathPrefix: 'content/alternatives/',
      type: 'alternative',
      intent: 'commercial',
    },
  ],
  pages: {
    'ahrefs-alternatives': {
      type: 'alternative',
      key: 'ahrefs-alternatives',
      slug: 'ahrefs-alternatives',
      title: 'Ahrefs Alternatives',
      intent: 'commercial',
      keywords: { primary: 'ahrefs alternatives' },
      linksTo: ['seo-tools-comparison'],
    },
  },
})

Page types

pageTypes are reusable contracts. Use them for page families such as docs pages, glossary entries, alternatives pages, comparison pages, product pages, or blog posts.

FieldMeaning
requiredFrontmatterDot paths that must exist after frontmatter normalization.
requiredSectionsHeadings that must appear in the document outline. Each section can provide allowed heading variants.
requiredBlocksBlock names that must appear at least once.
recommendedBlocksBlock names that produce informational suggestions when absent.
requiredLinksToLink targets that must appear in linksTo.
minOutgoingLinksMinimum number of outgoing SEO links.
minIncomingLinksMinimum number of backlinks from other scanned pages.

Frontmatter is normalized before SEO checks run. If a project uses seoKeywords, contentbit also exposes that data as keywords for page facts. That means a contract can require the source field (seoKeywords.primary) or the normalized field (keywords.primary), depending on the convention you want writers to see in the file.

Page defaults

pageDefaults classify existing files by path. Use them when a folder already means something:

pageDefaults: [
  { pathPrefix: 'content/docs/', type: 'docs-page', intent: 'documentation' },
  { pathPrefix: 'content/blog/', type: 'blog-post', intent: 'publication' },
]

Defaults fill in missing facts. Existing frontmatter and explicit planned page entries still win.

Planned pages

pages can describe pages before Markdown exists. That is the part that makes briefs useful for agents:

pages: {
  'content/alternatives/semrush.md': {
    type: 'alternative',
    key: 'semrush-alternatives',
    slug: 'semrush-alternatives',
    title: 'Semrush Alternatives',
    keywords: { primary: 'semrush alternatives' },
    linksTo: ['seo-tools-comparison'],
  },
}

When the file is later created, contentbit merges the plan with the live frontmatter and stats. Existing frontmatter wins for page facts such as title, slug, key, intent, keywords, and linksTo.

contentbit brief semrush-alternatives
contentbit brief semrush-alternatives "content/**/*.md"
contentbit brief semrush-alternatives "content/**/*.md" --json

Targets resolve by key first, then by slug, then by internal page id. With a project config, brief scans the configured content paths. If the page is only planned, the brief still prints.

The Markdown brief is meant to paste into an agent or editorial task. The JSON brief is better for tools.

Brief contents

A brief includes:

  • target page facts: id, source, key, slug, title, type, intent, and keywords
  • the matching page-type contract
  • required sections, blocks, and links
  • SEO findings for the target page
  • related pages from the same type or link neighborhood
  • acceptance checks the writer should satisfy before handoff

Studio shows the same data in the document Brief view, with copy and download actions for Markdown and JSON.

Doctor integration

When an SEO config exists, doctor includes SEO findings in the normal repair plan:

contentbit doctor "content/**/*.md" --strict-seo
contentbit doctor "content/**/*.md" --no-seo

Use --strict-seo in CI when missing contract requirements should fail the build. Use --no-seo when you only want validation, link, and stats checks.

Multilingual pages

Use the same link resolver options for brief, doctor, studio, and links.

contentbit brief glossary/recipe-import "content/**/*.md" \
  --link-resolve same-locale-key \
  --locale-field locale \
  --key-field key \
  --slug-field slug

For translated slugs, share a stable key across locales and localize slug. Contentbit keeps localized SEO pages separate while still resolving links by same-locale key.

On this page