
I recently added a blog to this site. Rather than reaching for a CMS, I decided to build it directly into the existing Next.js application using standard tools.
Requirements
I wanted a place to share technical thoughts without adding another system to maintain. The requirements were simple:
- Write in Markdown: I want to write posts in VS Code.
- Zero Runtime Cost: The blog should be statically generated.
- Fast: It should feel instant.
Markdown files and static routes
I created a simple utility, blogApi.ts, that reads directly from the file system. It uses gray-matter to parse frontmatter and a remark/rehype pipeline, including rehype-pretty-code, to process the content.
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.
The details that made it usable
I also spent time on a few details that made the whole thing feel finished:
- 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, meaning the highlighting is extremely accurate. Because it generates inline styles, there's no brittle CSS to import fromnode_modules. - Sitemap: A dynamic script crawls my posts directory to keep
sitemap.xmlup to date automatically.
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
Subscribe for occasional updates when I publish something new.
Related posts
Making Responsive Images Just Work
March 4, 2026
Instead of manually managing -sm/-md/-lg assets, I moved to a manifest-driven workflow that reduced duplication and made performance outcomes more consistent.
Relearning Through Small Interactive Tools
April 10, 2026
Using a coding agent to build small browser tools turned out to be a good way to revisit concepts I had not touched in a while.
Dropout as Implicit Bagging
March 7, 2026
Chapter 7 clarified that dropout works so well because it approximates bagging over many thinned networks with shared parameters.