Most Next.js blogs read MDX at request time, hand you frontmatter as any, and discover a missing date or a typo in a tag when the page 500s in production. That works until the content set grows past a handful of posts. Then every rename, every new frontmatter field, every tag cleanup becomes a guessing game, because nothing checks the shape of your content until a user hits the route.

This site works differently. Content is compiled once, validated against Zod schemas, and emitted as typed JSON that the app imports like any other module. If a post is missing a required field, the build fails — not the page. Here's how it's wired, taken straight from the config that ships this site.

The Problem with Reading MDX at Request Time

The common pattern looks like this: a getPostBySlug() helper reads a file, runs gray-matter, and returns an object. TypeScript has no idea what's in the frontmatter, so you either cast to any or hand-write an interface that drifts from reality the moment someone adds a field in an .mdx file.

Three things go wrong as the content grows: