---
title: Frontmatter
description: >-
  Required fields, group semantics, and how authored MDX becomes a navigation
  tree.
group: authoring
lastModified: '2026-05-11T20:02:32-07:00'
lastAuthor: 'github-actions[bot]'
---
# Frontmatter

Every page is a `.mdx` file with a YAML frontmatter block. Leadtype reads three things from it: what the page is, where it lives in the nav, and how to lint it.

## Minimum

```mdx
---
title: "Connect a docs site"
description: "Wire leadtype into a docs app build."
group: docs-site
---
```

* `title` — required. Non-empty string. Renders as the page heading and as the entry text in `llms.txt` and the sidebar.
* `description` — optional but recommended. Becomes the routing hint in `llms.txt`. If omitted, the converter synthesizes one from the body during conversion (see [convert behavior](/docs/reference/convert)).
* `group` — optional. Slug of a group declared in `docs.config.ts`. Pages without a group are excluded from the nav and grouped indexes, but still appear in generated markdown and the root `llms-full.txt` fallback.

## How groups become a nav tree

Each page may declare one or more group slugs with `group: <slug>` or `group: [a, b]`, so pages can be shared across multiple groups. Each group is declared once in `docs.config.ts`. `leadtype generate` loads that config automatically when it exists. The intersection produces the nav tree, the section headings in `llms.txt`, search metadata, and AGENTS.md grouping.

```mermaid
`flowchart LR
cfg["docs.config.ts / groups: [authoring, docs-site, reference]"]
page1["page A / group: authoring"]
page2["page B / group: docs-site"]
page3["page C / group: docs-site"]
resolver["group resolver"]
nav["navigation tree"]
llms["llms.txt sections"]
agents["AGENTS.md sections"]
cfg --> resolver
page1 --> resolver
page2 --> resolver
page3 --> resolver
resolver --> nav
resolver --> llms
resolver --> agents`
```

If a page declares a group slug that isn't in `docs.config.ts`, the build fails with `unknown group "<slug>"`. Same source of truth, no drift.

If you do not have a `docs.config.ts`, the CLI infers groups from the `group:` values it finds. That fallback is enough for small docs sets, but use config when you need stable order, descriptions, or nested groups.

### Nested groups

Declare children in the config to build deeper trees:

```ts
{
  slug: "docs-site",
  title: "Build a Docs Site",
  children: [
    { slug: "remote-source", title: "Remote source" },
    { slug: "connect-docs-site", title: "Connect a docs site" },
  ],
}
```

A page sets `group: remote-source` and lands in that nested slot. Only **leaf** groups (no children) directly contain pages; non-leaf groups are headings only.

## Optional fields

The default lint schema also accepts:

|Property|Type|Description|Default|Required|
|:--|:--|:--|:--|:--:|
|icon|string|Icon name resolved by your sidebar component.|-|Optional|
|deprecated|boolean|Marks the page as deprecated in nav and search.|-|Optional|
|deprecatedReason|string|Short message paired with deprecated.|-|Optional|
|experimental|boolean|Marks the page as experimental.|-|Optional|
|canary|boolean|Hides from stable channels.|-|Optional|
|new|boolean|Highlights the page as recently added.|-|Optional|
|draft|boolean|Excludes from generation entirely.|-|Optional|
|tags|string\[]|Free-form tags for search facets.|-|Optional|
|availableIn|Array\<\{ framework, url?, title? }>|Cross-framework availability map for TopicSwitcher pages.|-|Optional|
|full|boolean|Layout hint for docs UIs that support full-width pages.|-|Optional|

`lastModified` and `lastAuthor` are filled in automatically when you pass `--enrich-git` to the CLI. Don't author them by hand.

## Lint rules

`leadtype lint` enforces the schema, so violations surface in CI before they reach a build. The relevant rules:

* `schema` — a required field is missing or has the wrong type.
* `unknown-field` — a top-level field isn't in the schema (warn by default; `--error-unknown` to fail).
* `parse-error` — frontmatter or `meta.json` doesn't parse.
* `invalid-link` — a `/docs/...` link points to a route that doesn't exist.
* `unresolved-placeholder` — a doc URL still contains an unresolved `{framework}` placeholder.
* `cross-framework-link` — a framework-scoped page links to another framework's docs.

See [Lint rules](/docs/reference/lint) for the full reference and how to extend the schema.

## What this gives you

Once frontmatter is consistent, the rest of the pipeline works without per-page configuration. The same `group:` value drives:

* The sidebar position
* The `llms.txt` section
* Search metadata and AGENTS.md grouping
* The search filtering UI (if you build one)
* Cross-framework link checks in lint

One field, one source of truth.
