Content pages are Markdown files inside the content/ directory. Add optional frontmatter at the top of a file to set the title, date, tags, and other metadata. Sarde infers most fields when they are missing.
Create a page
Create the file by hand, or let sarde new write it with starter frontmatter:
sarde new docs "Lesson Planning"→ Sarde creates content/docs/lesson-planning.md containing a title, the current date, and draft: true.
The new page is a draft, so sarde build leaves it out. Delete the draft: true line when the page is ready to publish. See Drafts.
Frontmatter basics
Frontmatter is a metadata block at the top of a Markdown file. The most common fields:
---title: Photosynthesisdescription: How plants convert light to chemical energydate: 2026-03-15tags: [biology, plants]draft: false---Markdown content starts here.Every frontmatter field is optional. Without a title, Sarde uses the first # Heading in the content, or the filename converted to title case. A page with no title in its frontmatter still builds, but the build prints a <file>: is required warning for it.
See Frontmatter for the complete field reference.
Frontmatter formats
Sarde reads three frontmatter formats. YAML is the most common.
---title: Photosynthesistags: [biology, plants]---+++title = "Photosynthesis"tags = ["biology", "plants"]+++{ "title": "Photosynthesis", "tags": ["biology", "plants"]}The opening delimiter selects the format: --- for YAML, +++ for TOML, { for JSON. All three produce the same page.
Auto-inferred fields
Sarde fills in missing frontmatter from the filesystem and the content, so a file with only Markdown still builds into a complete page.
| Field | Inference chain |
|---|---|
title |
Frontmatter, then first # Heading, then filename title-cased |
slug |
Frontmatter, then filename (date and numeric prefixes stripped, slugified) |
date |
Frontmatter, then a YYYY-MM-DD- filename prefix, then file modification time |
updated |
Frontmatter, then git commit date or file modification time (per build.last_updated) |
order |
Frontmatter sidebar.order, then numeric filename prefix (for example 01-intro.md sets order to 1), then 0 |
The page URL comes from the filename, not from slug. A frontmatter slug changes the URL only when the collection sets a permalink pattern that uses :slug (see Content and Collections).
Numeric filename prefixes
Prefix a filename with a number and a hyphen or underscore to control sidebar order without frontmatter:
content/docs/ 01-getting-started.md # order: 1, slug: "getting-started" 02-installation.md # order: 2, slug: "installation" 03-configuration.md # order: 3, slug: "configuration"The prefix is stripped from the URL: 01-getting-started.md produces /docs/getting-started/, not /docs/01-getting-started/. Directory names keep their prefix in the URL, so content/docs/01-setup/install.md is served at /docs/01-setup/install/.
Date filename prefixes
A blog-style name that starts with a date sets the page's date, and the date is stripped from the URL:
content/blog/ 2026-03-15-hello-world.md # date: 2026-03-15, served at /blog/hello-world/ 2026-04-02-01-intro.md # date: 2026-04-02, order: 1, served at /blog/intro/Sarde 1.5 and earlier kept part of the date in the URL: 2026-03-15-hello-world.md was served at /blog/03-15-hello-world/. The build redirects that old URL to the current one, the same way as a page's aliases, so published links keep working. A page, an alias or a global redirect at the old path takes priority. Collections with a permalink pattern get no redirect, because their URLs never contained the date prefix.
A frontmatter date takes priority over the filename. Two posts in one folder whose names differ only by the date, such as 2025-01-10-recap.md and 2026-01-10-recap.md, resolve to the same URL. The build warns about the collision and keeps the first; rename one, or give the collection a permalink pattern with :year.
A filename made only of characters outside the Latin alphabet, such as 日本語.md, has no slug to strip down to. Its slug and URL use the first 8 hex characters of a hash of the name instead: /blog/a1b2c3d4/. For a readable URL, name the file in Latin characters, or set slug in frontmatter and give the collection a permalink pattern with :slug.
Page bundles
To keep a page's images and downloads next to its Markdown, make the page a page bundle: a directory that contains an index.md file and the asset files. Reference each asset by its filename:
Drafts, scheduled, and expiring content
Three frontmatter fields control when a page appears in the built site.
| Field | Excluded from sarde build when |
Include it with |
|---|---|---|
draft: true |
Always | --drafts (or -D), or build.drafts: true |
publish_date |
The date is in the future | --future, or build.future: true |
expiry_date |
The date has passed | build.expired: true |
Drafts
Mark a page as a draft:
---draft: true---sarde build excludes draft pages. sarde dev includes them by default; pass --no-drafts to exclude them. To publish drafts in a build, pass --drafts or set build.drafts: true in sarde.yaml.
Scheduled content
Set publish_date to hold a page back until a date:
---publish_date: 2026-06-01---Both sarde build and sarde dev exclude a page with a future publish_date until that date passes. Pass --future to either command to include it.
Expiring content
Set expiry_date to retire a page on a date:
---expiry_date: 2026-12-31---sarde build excludes a page once its expiry_date has passed, which suits time-limited announcements and seasonal content. sarde dev still shows expired pages so you can edit them.
When a page appears locally but not in production, check its frontmatter for draft: true or a past expiry_date.
Heading IDs and anchor links
Sarde assigns an id attribute to each heading in the configured range (h2 through h4 by default). The ID is the slugified heading text, so ## Getting Started becomes <h2 id="getting-started">. These IDs are the targets for fragment links (/docs/guide/#getting-started), table of contents entries, and search index anchors.
To set a custom ID, add the {#custom-id} attribute:
## My Section {#custom-id}→ The heading renders as <h2 id="custom-id"> instead of the generated slug.
When site.heading_links is true (the default), each heading also gets a clickable anchor link for sharing a direct URL to that section.
Configuring the heading range
To include h5 and h6 headings, widen the range:
markdown: toc: min_heading_level: 2 max_heading_level: 6Headings outside the range get no ID, no anchor link, and no table of contents entry. See Configuration for details.