Writing Content
How the file-based content model works and how to write MDX pages.
Content directory
All content lives in content/ at the project root. The URL path mirrors the
filesystem path exactly:
| File | URL |
|---|---|
content/index.mdx | / |
content/about.mdx | /about |
content/blog/post.mdx | /blog/post |
content/blog/ (directory) | /blog (article listing) |
Excluding files with _
Files and directories whose names start with _ are ignored by all content
pipelines — they get no public URL and never appear in listings, the
::article-list directive, the sitemap, or the RSS feed. Use this prefix for
anything under content/ you don't want published, such as content/_nav.mdx.
Frontmatter
Every MDX file should start with a YAML frontmatter block:
---
title: My Page # required
description: A short blurb. # required
publish-date: 2026-01-15 # optional — shown in header and listing cards
last-edited: 2026-02-01 # optional — shown in header if different from publish-date
author: your name # optional
tags: [one, two] # optional
---last-edited falls back to the file's filesystem mtime when omitted. It is only
displayed in the article header if it differs from publish-date.
Directory listings
A directory without an index.mdx automatically renders a listing of its MDX
files, sorted by publish-date descending (undated files sort last). No
configuration needed. If no articles are found the route returns 404.
The listing is non-recursive when the directory contains at least one .mdx
file directly. If the directory contains only subdirectories, the listing
becomes recursive and collects articles from the entire subtree.
To embed a listing inside any MDX file, use the ::article-list directive:
::article-list{dir=blog} ::article-list{dir=blog limit=5}MDX
Any valid Markdown is also valid MDX. You can embed JSX components anywhere in the body. The built-in components are documented in MDX Directives.
Math
Inline math with $...$ and block math with $$...$$:
Euler's identity: $e^{i\pi} + 1 = 0$
$$
\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}
$$Code blocks
Fenced code blocks are highlighted by Shiki. Specify the language after the opening fence:
```ts
const greeting: string = 'hello';
```