What is wired · 2 min
Blog
Posts are MDX files, not database rows, which means the blog stays up when the database does not.
content/blog/hello.mdx:
---
title: Why we rebuilt onboarding
description: It took four screens. Now it takes one.
date: 2026-09-01
tags: [product]
---
The first version asked for a company name before it asked for anything
useful.That is a published post. No admin screen, no editor, no row to insert.
Why files
A blog backed by the database goes down when the database does. It also cannot
be reviewed in a pull request, cannot be reverted with git revert, and cannot
be edited by the AI tools you already have pointed at this repo.
The trade is that publishing means deploying. For a product blog that ships a post a week, that is not a real cost.
Reading them
import { allPosts, postBySlug } from "@/lib/blog";
const posts = await allPosts(); // newest first, sorted on date
const post = await postBySlug("hello"); // null if there is no such fileallPosts reads the directory once and caches. Files cannot change at runtime
on a serverless host, so there is nothing to invalidate.
Rendering
The route compiles MDX at build time and maps the elements to your components, so a post inherits the site's typography instead of arriving with browser defaults.
export const dynamicParams = false;
export async function generateStaticParams() {
return (await allPosts()).map((post) => ({ slug: post.slug }));
}dynamicParams = false makes an unknown slug a 404 rather than an attempt to
render a file that is not there.
Adding syntax highlighting
If your posts contain code, add Shiki as a rehype plugin and emit both themes as CSS variables, so highlighted code follows the light and dark switch without shipping a highlighter to the browser:
rehypePlugins: [[rehypeShiki, {
themes: { light: "vitesse-light", dark: "vitesse-dark" },
defaultColor: false,
}]]That is exactly what these docs do.
Something wrong or missing on this page? Tell us.