
I added the blog directly to the site's existing Next.js application so technical notes could live beside the rest of the site without another system to maintain. Posts are Markdown files, each route builds to static HTML, and publishing does not depend on a CMS or runtime admin surface.
Markdown in, static HTML out
Three constraints shaped the implementation:
- Repo-backed Markdown: posts should live beside the rest of the site.
- Static output: each post should build into HTML during deploy.
- No admin runtime: publishing should not require a CMS, database, or editor surface.
File-backed routes at build time
I created a simple utility, blogApi.ts, that reads directly from the file system and uses gray-matter to parse frontmatter. A separate markdownToHtml.ts pipeline converts post bodies with remark and rehype, including rehype-pretty-code.
Next.js makes it straightforward to turn a folder of markdown files into routes. I used generateStaticParams to tell Next.js which paths to build at compile time:
export async function generateStaticParams() {
const posts = getAllPosts(["slug"]);
return posts.map((post) => ({
slug: post.slug,
}));
}
This ensures that alexleung.ca/blog/boring-blog-architecture/ is just a static HTML file at deploy time, not a dynamic request.
Small details keep publishing simple
The details that mattered were small, but they made the blog easier to live with:
- Typography: I used
@tailwindcss/typographybut customized it to remove the default backticks from inline code for a cleaner look. - Metadata: Each post automatically generates its own SEO tags and JSON-LD structured data.
- Syntax Highlighting: I chose
rehype-pretty-code(powered by Shiki). It uses the same TextMate grammars as VS Code, so the highlighting is close to what I am used to in the editor. It also generates inline styles, so there is no separate CSS import to manage. - Sitemap: A force-static Next.js metadata route uses the same post loader at build time, so
sitemap.xmlincludes each post and its publication or update date.
The result is simple on purpose. I can write in Markdown, keep everything in the repo, and ship a blog with a rendering path that is easy to understand.
Get new posts by email
Occasional updates when I publish something new.
Follow.it sends the emails and includes an unsubscribe link.
Related posts
One Manifest for Responsive Images
March 4, 2026
I replaced scattered responsive-image conventions in a Next.js static site with one generated manifest for variants, dimensions, and runtime lookup.
Camping Indoors in San Francisco
April 27, 2026
My first week in San Francisco: paperwork, an unfurnished apartment, two cats, Bay Trail runs, and waiting for furniture.
Coding Agents for Inspectable Browser Tools
April 10, 2026
Building frontend-only tools for load flow and Mandelbrot zooming gave me concrete ways to inspect older technical models.