Skip to main content
Nicolás Kennedy

Type-safe frontmatter with Zod

  • mdx
  • typescript
  • zod

← back to blog list

Every post on this site declares frontmatter — a title, a date, a description, maybe some tags. The problem with frontmatter is that it is just untyped data sitting at the top of a file, easy to get subtly wrong.

One schema, two jobs

A single Zod schema does double duty: it validates the frontmatter at build time, and it is the source of the TypeScript type via z.infer, so the runtime contract and the compile-time type can never drift apart.

import { z } from 'zod'
 
export const frontmatterSchema = z.object({
  title: z.string().min(1),
  date: z.coerce.date(),
  description: z.string().min(1),
  tags: z.array(z.string()).default([]),
  draft: z.boolean().default(false),
})
 
export type Frontmatter = z.infer<typeof frontmatterSchema>

Tip

Using z.coerce.date() means a quoted string and a YAML date both parse the same way — and an unparseable value fails validation, which fails the build.

If a post is missing a required field or has a malformed date, the build stops and tells you which post. A broken post never makes it to production.