MDX Content
How the marketing site renders local MDX content for the blog and changelog: where files live, the frontmatter contract, Zod validation, and draft handling.
The marketing site (apps/marketing) ships a small, dependency-light MDX content
system that powers the blog and changelog.
It is intentionally local — no CMS, no database — so content ships and versions
with the repo.
Where content lives
apps/marketing/content/
blog/<slug>.mdx
changelog/<slug>.mdxThe filename (minus .mdx) is the URL slug. The loaders are in
apps/marketing/src/lib/content/.
How it works
- Frontmatter is parsed by a minimal first-party parser
(
lib/content/frontmatter.ts). It supports exactly the shapes the content uses: quoted/plain scalars, inline arrays ([a, b, c]), and booleans. - Validation is done with Zod (
lib/content/util.ts,blog.ts,changelog.ts). A malformed or incomplete file fails the build loudly rather than rendering broken output. - Dates are authored as plain
YYYY-MM-DDstrings. - An empty content directory renders an honest empty state — you can delete all posts without breaking the listing pages.
Drafts
Set draft: true in a file's frontmatter to keep it visible during local
development (bun run dev:marketing) but excluded from production builds, the
sitemap, and listings. Flip it to false (or remove it) to publish.
Adding content
See Blog and Changelog for the exact frontmatter each type expects, and SEO and Metadata for how pages get canonical URLs and Open Graph tags.
To remove the system entirely, see Removing Blog and Changelog.