From Markdown to typed pages: the build pipeline
A content-driven site is really a small compiler. Text files go in, HTML comes out, and a schema in the middle keeps the two honest. Here is the shape of it.
The diagram above is written in Mermaid — just a fenced ```mermaid block —
and rendered to a plain SVG at build time, so the page ships no diagramming
JavaScript and still works offline.
The schema is the contract
Every post is validated against a schema before it becomes a page. Get a field wrong and the build stops — you never ship a half-broken post.
// src/content.config.ts
const posts = defineCollection({
loader: glob({ pattern: '**/index.{md,mdx}', base: './src/content/posts' }),
schema: z.object({
title: z.string(),
pubDate: z.coerce.date(),
category: z.string(),
}),
});
Rendering is boring, on purpose
Once entries are typed, turning them into pages is mechanical. No surprises at runtime because there is no runtime — it already ran, at build.
---
const posts = await getCollection('posts');
---
{posts.map((p) => <a href={`/posts/${p.id}`}>{p.data.title}</a>)}
The payoff: a broken link or a missing date is a build error on my machine, not a bug a reader finds later.