Building This Blog: A Modern Next.js Blog with Markdown
How I built this blog using Next.js, TypeScript, Tailwind CSS, and markdown for content management. A complete guide to creating a fast, SEO-optimized blog.

Building This Blog: an MDX‑powered Next.js blog
This blog is a small, opinionated setup for writing long‑form content that feels good to read and fun to build on.
Under the hood it uses static generation, MDX, and a theme‑aware UI that works in both light and dark modes.
This article runs on MDX
Everything after the frontmatter is pure MDX. Components like Callout, Steps, architecture diagrams, flows, and charts are all React components rendered directly in the post body.
Stack I ended up with
I wanted something fast, simple, and easy to reason about over years—not weeks.
Stack at a glance
- ●
Next.js 15 + App Router
The site is a static App Router app. Routes like
/blog/[slug],/til/[slug],/technical-terms/[slug]are statically generated at build time. - ●
TypeScript + Tailwind CSS
TypeScript keeps the content layer and components honest. Tailwind + CSS variables handle layout and theming.
- ●
Markdown & MDX content
All long‑form content lives in the repo inside content/, written as
.mdor.mdxfiles with frontmatter. - ●
Supabase analytics
Views, upvotes, and UTM events are stored in Supabase and surfaced through a custom analytics dashboard.
How content works
All posts are simple files in the repo—no CMS required.
---
title: "Your Post Title"
description: "Post description"
date: "2024-01-20"
author: "Your Name"
tags: ["tag1", "tag2"]
category: "Category"
featured: true
---
Your content here...
- Markdown (
.md) posts go through a Remark pipeline and are rendered as HTML. - MDX (
.mdx) posts are compiled at runtime in the page usingnext-mdx-remote/rsc, with a shared component map.
MDX for richer articles
For posts that need diagrams, flows, or charts, I use .mdx and drop in components like ArchitectureCard, FlowStep, and DemoBarChart.
Authoring flow
From idea to published post
- ●
1. Create the file
Add a new
.mdor.mdxfile to content/blog/ with frontmatter for SEO, tags, and images. - ●
2. Preview locally
Run npm run dev and iterate on copy, diagrams, and layout until the post feels polished.
- ●
3. Ship with a push
Push to the main branch. The build step generates static HTML for every route and updates search data + OG images.
Architecture of the blog
At a high level, the blog is just three pieces: browser, Next.js, and Supabase.
High-level architecture
Browser
Reader experience
Readers get fully rendered HTML from the CDN, with minimal JavaScript on top for upvotes, analytics, and small interactions.
Next.js app
Build & routing
Next.js statically generates pages for blog posts, TIL entries, technical terms, and lists. It also exposes a few API routes for analytics.
Supabase
Analytics & stats
Supabase stores per‑page views, upvotes, and UTM events that feed the analytics dashboard and charts.
Request–response flow for a page view
Here’s how a single blog page view flows through the system.
Page view lifecycle
1. Reader opens /blog/[slug]
The CDN serves a pre‑rendered HTML page that was generated at build time.
2. Client enhances the page
Next.js hydrates the page; components like the upvote button, custom cursor, and view counter start working.
3. Analytics event is recorded
A lightweight request records the view / upvote in Supabase without blocking the reader.
4. Dashboard visualizes it
Aggregated stats are used to power Recharts visualizations on the analytics page.
Visualizing traffic with charts
The same Recharts setup used in the analytics dashboard is available inside MDX posts.
Charts inside content
Charts are plain React components exposed to MDX. They respect the global theme and use the same accent palette as the rest of the site.
Performance before and after
Static export plus small tweaks around assets made a noticeable difference in user‑facing numbers.
Before vs after optimizations
Why this setup works for me
- Files as the source of truth – I can write posts in a text editor, version them in git, and refactor them like any other code.
- Static by default – Most pages are static HTML, which is great for speed and reliability.
- MDX when I need power – Architecture diagrams, flows, and charts are just components; I only reach for them when a post really needs them.
- Theme‑aware UI – Components use the same CSS variables as the rest of the app, so they look good in both light and dark modes.
If you want to explore the implementation details, the full source code is on GitHub. You could clone it, point it at your own content folder, and have a similar blog running in minutes.
Tags
Share this post
Backend engineer at Initializ.ai — building scalable systems with Go, Elixir, and Kubernetes. Writing about distributed systems, AWS, and the bugs that cost me hours.
