Leave Localhost logoLeave LocalhostDocs
Content

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>.mdx

The 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-DD strings.
  • 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.

On this page