Writing Content
Frontmatter, the full meta.json grammar, route groups, locales, and heading controls.
Content is a folder of Markdown and MDX files. PzzaDocs reads every .mdx and .md file under contentDir, parses the frontmatter, and compiles the body with full MDX support: JSX, expressions, and GitHub Flavored Markdown tables, task lists, strikethrough, and autolinks.
Frontmatter#
Each file starts with a YAML block. title is the only required field.
---
title: Setup
description: Install and configure the CLI.
icon: Wrench
order: 2
full: false
tag: guides
---
## First heading
Body text goes here.| Name | Type | Default | Description |
|---|---|---|---|
title* | string | - | Page title. Used for the h1, the sidebar label, breadcrumbs, pagination, and search. |
description | string | - | One-line summary rendered under the title and used in metadata and search. |
icon | string | - | Icon name shown next to the sidebar entry. Any icon from the bundled set, in PascalCase or kebab-case. |
full | boolean | false | Layout mode: the article spans the TOC column and the TOC becomes the popover bar at every width. |
order | number | - | Sort key used when a folder has no meta.json or when "..." expands the remaining pages. |
tag | string | - | Free-form tag exposed on every search record of the page, for filtering. |
Any other keys are preserved on the page object under frontmatter, so you can read custom fields from your own route code.
Folder routes and route groups#
A file named index.mdx maps to the folder itself: content/docs/guides/index.mdx is served at /docs/guides.
Folders whose name is wrapped in parentheses, such as (advanced), are route groups. They organise the sidebar without appearing in URLs: content/docs/(advanced)/caching.mdx is served at /docs/caching. Two files that resolve to the same URL throw at build time, so the conflict is caught early. A route group is never a link itself, even when it contains an index.mdx: the group renders as a plain section label and its index page is listed as an ordinary entry, so the (getting-started)/index.mdx pattern gives you a grouped sidebar whose first item is the docs root.
Locales#
Locale variants sit next to the default file with a suffix: setup.de.mdx and meta.de.json. Declare the locales in the config, then pass the locale to the source methods:
// docs.config.ts
locales: ["de", "fr"],
defaultLocale: "en",
// in a route
const page = await source.getPage(slug, "de");Missing translations fall back to the default file, so a partially translated site still renders every page. Translations can also live in one directory per locale (content/docs/en/..., content/docs/de/...) with localeStrategy: "directory", and localePrefix puts the locale into page URLs. The Localization guide covers both strategies, the route wiring, and translated UI strings.
Ordering with meta.json#
Each folder can contain a meta.json that labels the folder in the sidebar and controls its children.
{
"title": "Guides",
"description": "Step by step guides.",
"icon": "BookOpen",
"collapsible": true,
"defaultOpen": true,
"root": false,
"pagesIndex": "index",
"pages": [
"index",
"---[Star]Basics---",
"quickstart",
"...advanced",
"!internal",
"[Rocket][Changelog](/changelog)",
"external:[Repository](https://github.com/pzzaworks/pzza-docs)",
"..."
]
}Folder fields#
| Name | Type | Default | Description |
|---|---|---|---|
title | string | - | Label shown for the folder. Defaults to the folder name. |
description | string | - | Short text carried on the tree node (PageTreeFolder.description) for custom sidebars and listings. |
icon | string | - | Icon name shown next to the label. |
collapsible | boolean | true | When false, the folder always shows its children and has no chevron. |
defaultOpen | boolean | - | Expanded on first render. Overrides sidebar.defaultOpenLevel for this folder. |
root | boolean | string | false | true makes the folder a sidebar root. A string names the root type so same-type roots are interchangeable. See the sidebar guide. |
pagesIndex | string | "index" | File name (without extension) of the folder's clickable page, or a [Text](url) link item. |
pages | string[] | - | Explicit ordering. See the entry grammar below. |
Entry grammar#
| Entry | Meaning |
|---|---|
"name" | A page or folder in this directory, by file name without extension. "index" is the folder's index page. |
"..." | Every remaining item, sorted by order and then by name. |
"z...a" | The same as "..." in reverse order. |
"!name" | Excludes name from the rest. Use it with "..." to hide a page without deleting it. |
"...folder" | The children of folder hoisted inline, without the folder itself. |
"[Text](url)" | A link item. url can be internal or external. |
"[Icon][Text](url)" | A link item with an icon. |
"external:[Text](url)" | A link item that always opens in a new tab with an external icon. |
"---Label---" | A section label: small uppercase text that does not link anywhere. |
"---[Icon]Label---" | A section label with an icon. |
When pages is given, items that are not listed are hidden unless "..." appears somewhere in the array.
No meta.json?
Folders without a meta.json behave as if pages were ["..."]: the index page comes first, then the remaining pages sorted by order, then alphabetically.
The sidebar on this site is built from these rules. The Components folder uses ---[Icon]Label--- separators to group pages, and the API Reference folder ends with an external: link to the npm package.
Headings and the table of contents#
Headings receive a stable id derived from their text, so ## Install the CLI can be linked as #install-the-cli. Hovering a heading reveals an anchor link. Three suffixes control the behaviour:
| Syntax | Effect |
|---|---|
## Title [#custom-id] | Uses custom-id as the anchor instead of the generated slug. |
## Title [!toc] | Renders the heading but hides it from the table of contents. |
## Title [toc] | Adds a TOC entry without rendering the heading. Useful when a component renders its own heading. |
The depth range collected into the TOC comes from toc.minDepth and toc.maxDepth in the config (defaults 2 and 3).
A custom anchor in practice#
This heading is written as ### A custom anchor in practice [#anchor-demo], so its link is #anchor-demo.
Links#
Use absolute paths that include baseUrl for internal links. They render through next/link, so navigation is client-side and prefetched:
See the [configuration reference](/api/config).Images#
Plain Markdown images open in a lightbox when clicked. See Image Zoom.
Code#
Fenced code blocks are highlighted at compile time with the bundled monochrome themes. The fence meta supports titles, tabs, line numbers, and notation comments for highlighting and diffs. See Code Blocks for the full grammar.
Components in MDX#
All components from pzzadocs/mdx are available in every page without an import statement:
<Callout type="warn" title="Heads up">
This operation cannot be undone.
</Callout>Register your own with createSource(config, { components: { Chart } }).
Search index#
The search index is built from the frontmatter, headings, and body text of every page, so descriptive titles and a description on each page produce better results. See Search.