The Late Night Project logoThe Late Night Project

Build Log Entry

Building the MDX Blog System

2 min read

How I set up MDX-powered posts with frontmatter parsing, slug generation, and a utility layer that keeps the rest of the app clean.

mdxnext.jstypescriptarchitecture

title: "Building the MDX Blog System" date: "2025-01-22" description: "How I set up MDX-powered posts with frontmatter parsing, slug generation, and a utility layer that keeps the rest of the app clean." tags: ["mdx", "next.js", "typescript", "architecture"]

Building the MDX Blog System

Every developer blog needs a content system. The question is whether you reach for a headless CMS, a third-party platform, or build your own. I went with my own — simple, file-based, MDX-powered.

Here's exactly how it works.

Why MDX?

MDX lets you write Markdown with embedded React components. That means:

  • Long-form prose stays readable in the source files
  • I can drop in custom components (code blocks, callouts, diagrams) without leaving the flow of writing
  • Everything lives in git — no databases, no external dependencies, no vendor lock-in

The tradeoff is that setup takes more work upfront. Worth it.

The File Structure

Posts live in src/content/build-log/. Each file is named with a zero-padded index and a slug:

src/content/build-log/
  001-launching-the-late-night-project.mdx
  002-building-the-mdx-blog-system.mdx
  003-design-system-foundations.mdx

The numbering keeps them ordered chronologically in the file system, which is nice when you have a lot of posts.

Frontmatter Schema

Each post has a consistent frontmatter block:

---
title: "Post Title Here"
date: "YYYY-MM-DD"
description: "One or two sentence summary for SEO and previews."
tags: ["tag1", "tag2", "tag3"]
---
  • title — Display title, also used for <title> and OpenGraph
  • date — ISO 8601 date string for sorting and display
  • description — SEO meta description and card preview text
  • tags — Array of string tags for filtering and categorization

The Utility Layer

The src/lib/posts.ts file exposes a clean API:

// Get all posts sorted by date (newest first)
getAllPosts(): BuildLogPost[]

// Get a single post by slug with full content
getPostBySlug(slug: string): BuildLogPost | null

Under the hood, it uses gray-matter to parse frontmatter from the MDX files and fs to read from disk at build time. The content stays as a raw string until the page component needs to render it.

What's Next

The utility layer is in place. Next up is building the actual /build-log page that lists all posts, and individual post pages that render the MDX content with a custom component set.

The interesting challenge there will be the code syntax highlighting. I'm looking at Shiki for that — it runs at build time and ships zero JavaScript for highlighting, which is the right call for a static-leaning site.