GenerateSaaS

Authoring content

Base frontmatter for every content document, in-body shortcodes, categories and authors, hand localization, and the optional Content API.

Drop a .md or .mdx file in packages/content/{locale}/{section}/. The path is the URL: en/blog/hello-world.md serves /blog/hello-world, and in a page or alternatives section index.md serves /{section} itself. Every section shares the frontmatter below; the alternatives page adds required fields of its own.

Base frontmatter

FieldTypeNotes
titlestringRequired. The H1.
dateYYYY-MM-DDRequired on every document, any template. Sort order and datePublished.
descriptionstringRequired in practice: the excerpt is description, else seo.description, else empty.
dateModifiedYYYY-MM-DDShown as "Updated" and emitted in JSON-LD.
lastVerifiedYYYY-MM-DDWhen you last checked the facts. Required by the alternatives page.
primaryKeywordstringOne page per query per locale; a second claimant is a guard error, earlier date wins.
authorstringMust match a slug in blogAuthors.
categorystringArticle sections only. Must match a slug in contentCategories[section].
tagsstring[]Free-form. Tag archives are noindex, follow, out of the sitemap.
featured / draftbooleanThe newest one fills the featured slot at the top of the section homepage and is the only document held out of the blocks below it / hidden from every listing and feed.
image / imageAltstringHero image and alt text, used as the social preview instead of the generated card. Add imageWidth / imageHeight.
imagePositionImagePositionPer-document override of config.content.defaults.imagePosition.
featuresPartial<ContentFeatures>Display overrides, see Content sections.
faq{ q, a }[]Rendered as native <details> and as FAQPage JSON-LD.
relatedstring[]Sibling slugs to cross-link first.
articlebooleanpage template only: advertises og:type: article and adds an Article node to the page's JSON-LD.
translatebooleanfalse keeps the translator away from the file.
seo{ title?, description?, noindex? }Per-document metadata overrides.

Shortcodes in the body

Usable in any body with no import; they read live config, so no page holds a stale value.

ShortcodeRenders
<Related slug="supastarter" />In-body link to a sibling in the SAME section, carrying its title. An unresolved or draft slug renders nothing.
<CompareTable headers rows caption compact />A prose comparison table - see Alternatives page.
<Callout type="warn" title="..." />A boxed note.
<SiteName />, <BaseUrl />, <SupportEmail />, <BusinessName />, <BusinessAddress />, <RegistrationNumber />Config values.

Categories, authors, section copy

Category: add the slug to that section's array in contentCategories (packages/config/src/content.ts), then add content.categories.{section}.{slug} with name and description to packages/i18n/translations/en/web.json. Only edit en; other locales are generated.
Author: add the object to blogAuthors (packages/config/src/blog.ts) with slug and name, plus optional jobTitle, bio, avatar and social. Authors are site-wide.
Section copy: for a new section, set content.sections.{slug} with name, description, meta_title and meta_description. Every key falls back, so a new section never 500s.

Only categories with a published document appear in listings and the sitemap, and a category needs three or more before it earns its own block on the section homepage - see Content sections.

Localizing by hand

The pre-commit translate hook mirrors en into every locale. Two escapes:

  • translate: false in the frontmatter, bare and lowercase. The translator skips the source; a copy made before you set the flag stays as it is.
  • _-prefixed filenames. _template.md is never translated, collected or served.

Structured template frontmatter translates badly: copy the file into the target locale and translate it yourself.

The Content API

config.contentApi serves the same Markdown over HTTP for crawlers and automation. Generated projects ship { enabled: false }; the routes answer 404 until you turn it on.

  • Every endpoint needs the X-Content-Api-Key header, compared timing-safely against CONTENT_API_KEY (min 16 chars; 503 unset, 401 wrong).
  • GET/POST/PUT/DELETE on /v1/content/:collection[/:slug], plus GET /v1/content/blog/{categories,authors} - see API Layer.
  • Slugs are validated against ^[a-z0-9-]+$ with path-traversal confinement, so nested documents are unreachable and _ files never list. Writes: 10/min.

With gitCommit: true each write auto-commits via simple-git, which needs a writable repository. On read-only serverless filesystems commits fail silently and the changes are lost on the next deploy.

On this page