Skip to content
Blume
Esc
↑↓navigate↵open⌘Jpreview
On this page

Frontmatter

Every frontmatter field a page accepts, all optional — title, description, sidebar, SEO, search, and the rest, with what each one controls.

Every page accepts the following frontmatter. All fields are optional.

Frontmatter is YAML, so quote a value that holds a colon followed by a space, like description: "Setup: the easy way". Unquoted, YAML reads the second colon as the start of a nested key, and Blume reports the file and line as BLUME_FRONTMATTER_INVALID.

PropType
title?string

Page title.

Typestring
description?string

Page summary.

Typestring
type?string

Content type. blog/changelog drive feeds.

Typestring
Defaultdoc
date?string

Publish date for blog/changelog feeds (ISO or YAML date).

Typestring
authors?string | string[] | object[]

Post author(s) for blog/changelog content — a name, or objects with a name plus optional avatar/url and any extra fields. Credited as Person nodes in the page's JSON-LD; other fields are preserved as-is.

Typestring | string[] | object[]
slug?string

Set the page's full route from the content root (guides/setup). It replaces the whole path the file's location gives the page, not just the last segment. A . or .. segment is an error: browsers resolve it away, so no link could reach the page.

Typestring
draft?boolean

Exclude from production builds.

Typeboolean
Defaultfalse
deprecated?boolean

Mark the page deprecated: its sidebar row gets a deprecated pill (a translatable UI string).

Typeboolean
Defaultfalse
hidden?boolean

Shorthand for sidebar.hidden.

Typeboolean
Defaultfalse
noindex?boolean

Shorthand for seo.noindex.

Typeboolean
Defaultfalse
narration?boolean

Set to false to leave this page without the Listen to this page player when narration is on.

Typeboolean
Defaulttrue
related?(string | { [title]: string })[] | false

Pages to suggest at the foot of this one, up to ten: root-relative paths, absolute URLs, or { Title: link } to name a link. See Related pages below.

Type(string | { [title]: string })[] | false
pagination?boolean

Set to false to drop the previous and next links at the foot of the page. The page stays in the order, so its neighbors still link to it.

Typeboolean
Defaulttrue
icon?string

Lucide icon for the page's sidebar row when sidebar.icon isn't set (sidebar.icon wins).

Typestring
lastModified?string

Pin the page's "last updated" date (ISO or YAML date); overrides the git-derived date.

Typestring
mode?"default" | "wide" | "center" | "custom" | "frame"

What the page shows around its content. See Layout below.

Type"default" | "wide" | "center" | "custom" | "frame"
Default"default"
api?string

An endpoint this page documents by hand, as an HTTP method and a path or URL (POST /v1/users). See Hand-written API pages.

Typestring
authMethod?"bearer" | "basic" | "key" | "none"

How an api page's endpoint authenticates, over the site's api.auth.

Type"bearer" | "basic" | "key" | "none"
playground?"interactive" | "simple" | "none"

What an api page shows: the Try it panel and samples, the samples only, or neither.

Type"interactive" | "simple" | "none"
Default"interactive"

Layout

mode sets what a page shows around its content:

Mode Sidebar Title and table of contents Page-end links and footer Content width
default Yes Yes Yes The prose measure
wide Yes Title only Yes The whole column
center No Title only Yes A wider column, centered
frame Yes No No The whole column
custom No No No The whole page

wide suits a page of wide tables or diagrams, and center release notes or an announcement read top to bottom. custom keeps only the header, for a landing page written in MDX, and frame keeps the sidebar too, for a tool or dashboard that sits in the docs’ navigation. Neither renders the page’s title or description, so write your own heading. Every mode stays in search and keeps its Markdown copy.

title: Welcome
mode: custom

The values match Mintlify’s; its assistant mode, a full-page chat, has no equivalent.

related lists pages to suggest at the foot of this one, as cards under a Related pages heading:

related:
  - /guides/deployment
  - Search setup: /configuration/search
  - https://astro.build

A root-relative path names another page, which the card shows with that page’s title and description; on a translated page it links to the translation when there is one. A Title: link entry sets the card’s title itself, and an absolute URL links off the site under its host name. Paths are checked like any link on the page, so blume validate reports one that matches no page. The key matches Mintlify’s, so migrated pages keep it as written.

sidebar:
  label: Install
  order: 2
  icon: download
  badge: New
  hidden: false
  display: page

hidden removes the page from the sidebar and from previous/next pagination, and so from the sitemap, search, and llms.txt (see Hidden pages). On a folder’s index page it removes only the page’s own row: the group row keeps linking to the page, so previous/next links still pass through it and the page stays in the sitemap, search, and llms.txt. On the home page, the root index page, it also removes only the row: the page stays in the sitemap, search, and llms.txt.

display sets the render mode of the page’s folder group (per-group overrides) and is only meaningful on a folder’s index page under the generated sidebar — anywhere else (a non-index page, the content root’s own index page, or any page under an explicit navigation.sidebar) it has no group to configure, and Blume warns with BLUME_SIDEBAR_DISPLAY_IGNORED.

SEO

seo:
  title: Install Blume
  description: Install Blume and scaffold your first project.
  image: /og/install.png
  canonical: https://acme.com/install
  noindex: false
  x:
    creator: "@jane"

noindex emits a robots noindex, drops the page from the sitemap, and skips its structured data. A canonical that names another URL drops the page from the sitemap too, since the sitemap lists canonical URLs only. x.creator credits the page to an X account (twitter:creator) — a guest post’s author, say. See Metadata for every field.

search:
  exclude: false
  tags: [api]
  keywords: [install, setup]
  boost: 3

exclude keeps the page out of search, and tags labels its record for a hosted search adapter to filter on (see Tags). keywords are extra terms the page is found by, beyond its own text. boost multiplies the page’s relevance: above 1 ranks it higher, below 1 lower. See Ranking.

AI

ai:
  exclude: true

ai.exclude keeps the page out of llms.txt and llms-full.txt and out of the page map in your site’s skill. The page still renders, stays in search, the MCP server, and the JSON API, and keeps its place in the sitemap.

Changelog

Changelog entries (type: changelog) accept an optional changelog object for richer feed and display metadata:

type: changelog
changelog:
  version: 1.2.0
  date: 2026-06-20
  category: Features

date may live here or at the top level — both feed the changelog RSS feed. See Changelog for the generated timeline page and feed.

Custom keys

Any key outside this reference fails the build, so typos are caught early. Projects that carry their own metadata can opt extra keys in via frontmatter.extend in blume.config.ts, each validated by a schema the project supplies:

import { defineConfig } from "blume";
import { z } from "zod";

export default defineConfig({
  frontmatter: {
    extend: {
      owner: z.string(),
      reviewedAt: z.coerce.date().optional(),
    },
  },
});
---
title: Install
owner: "@sam"
reviewedAt: 2026-06-20
---

Schemas are accepted through the Standard Schema interface, so Zod (whichever version your project installs), Valibot, and ArkType all work. Every declared key is validated on every page — absent ones included — so a required schema enforces the key site-wide; mark it .optional() to validate only where present. All other keys stay strictly validated, and built-in fields can’t be redeclared.

Per-type keys

To require keys only on one content type — an RFC’s status, an incident report’s severity — declare them under content.types instead, keyed by the frontmatter type they apply to:

import { defineConfig } from "blume";
import { z } from "zod";

export default defineConfig({
  content: {
    types: {
      rfc: {
        frontmatter: {
          domain: z.string(),
          status: z.enum(["draft", "review", "enforced"]),
        },
      },
    },
  },
});
---
title: OpenAPI request schemas
type: rfc
domain: architecture
status: enforced
---

Per-type keys follow the same validation rules as extend, scoped to pages whose resolved type matches — including pages that set no type, when the declaration is for content.defaultType. A key belongs to one declaration, site-wide or per-type, not both. And a key declared only for another type stays unknown elsewhere, so a stray status on a plain doc page still fails the build.

A page that fails validation fails blume build with a diagnostic naming the file and key. With --no-strict, the build succeeds anyway and the failing pages are dropped from the output — the build summary reports how many.

Schemas are exported from blume/schema for editor and migration tooling.

Last updated on

Was this page helpful?