Building a Zero-Maintenance Blog with MDX and Next.js 15
How to build a git-powered blog using MDX, gray-matter, and Next.js App Router — no database required.

Modern portfolio sites often over-engineer their blog. You set up a CMS, configure webhooks, provision a database, and spend more time on infrastructure than writing. MDX with Next.js App Router eliminates all of that — your content lives in the repository, versioned alongside your code, with zero runtime dependencies.
Why MDX Over a Headless CMS
A headless CMS like Sanity or Contentful is the right choice when a non-technical team needs to publish content. For a developer's personal blog, it introduces unnecessary coupling:
- Cold-start latency — every page render hits an external API
- Availability dependency — your CMS going down takes your blog with it
- Context switching — writing in a browser editor instead of your IDE
MDX flips this. Content is a .mdx file. Deployment is a git push. There is no CMS to maintain.
Project Structure
content/
blog/
nextjs-15-architecture.mdx
trading-bot-design.mdx
lib/
mdx.ts ← parse frontmatter + content
app/[locale]/blog/
page.tsx ← listing
[slug]/page.tsx ← detail
Parsing MDX Files with gray-matter
The core of the system is a single utility that reads .mdx files from disk and maps them to your data model:
ts
gray-matter splits the YAML frontmatter from the body. The rest is plain TypeScript — no special MDX runtime needed at this stage.
Rendering MDX in the App Router
For the detail page, Next.js recommends @next/mdx or the lighter next-mdx-remote. Because we're loading files at request time (not statically importing), next-mdx-remote fits better:
ts
MDXRemote from the /rsc export is a React Server Component — it compiles and renders MDX on the server with zero client JavaScript.
Frontmatter Schema
A consistent frontmatter structure prevents edge cases in the listing page:
yaml
Keep published: false on drafts — the getMdxPosts filter excludes them without touching routing.
Static Generation with generateStaticParams
For maximum performance, pre-render all posts at build time:
ts
Combined with next/image for cover images and a CDN in front of Vercel, Time to First Byte drops to single-digit milliseconds.
Adding Custom MDX Components
One of MDX's biggest advantages is embedding React components in prose. Map component names to implementations in one place:
ts
Now any .mdx file can use <Callout> or <CodeBlock> inline.
Trade-offs
What you gain:
- No database, no CMS API, no webhooks
- Content versioned in git — drafts are branches, reviews are PRs
- Full IDE tooling: autocomplete, spell-check, refactoring
What you give up:
- No visual editor for non-technical collaborators
- No built-in media management (use Cloudinary or
public/) - Redeployment required to publish (acceptable for solo portfolios)
Conclusion
For a developer portfolio, an MDX-based blog is the right level of complexity. The setup fits in two files, scales to hundreds of posts without a database, and keeps the authoring experience inside the tools you already use. The only maintenance cost is an occasional npm update for gray-matter and next-mdx-remote.
Related articles
Restaurant Website with a Digital Menu: 2026 Best Practices
How restaurants win more guests with a fast website, a digital menu and online reservations — mobile-first, findable and appetising.
Read articleDentist Website 2026: Online Booking, Data Protection, Patient Trust
What a modern dental practice website needs: online appointment booking, GDPR-compliant handling of patient data, and trust that wins new patients.
Read article