Back to blogEngineering

MDX or a Headless CMS? Picking a Blog Backend for a Small Site

Sanity and Contentful are excellent tools most small sites do not need yet. How to decide, and how to keep the door open either way.

Anup Bhandari
4 min read
A writing desk with a notebook and laptop

Adding a blog to a company site starts as a content decision and turns into an architecture one about ten minutes in. Files in the repo, or a headless CMS?

The honest answer is that it depends on one thing, and it is not a technical thing.

The question that actually decides it

Who writes the posts?

If the answer is "the person who also has commit access," write MDX files. If the answer is "someone in marketing who will never open a terminal," use a CMS.

Everything else — build times, preview environments, image pipelines, content modelling — is downstream of that. Teams get this backwards constantly: they evaluate CMSs on features, pick a good one, and then discover the only person publishing is the developer who now has to leave their editor, open a browser, and paste Markdown into a rich text field to change a typo.

What MDX gets you

A post is a file:

---
title: 'Deploying Next.js to a VPS'
date: '2026-09-06'
category: 'DevOps'
---

Most Next.js tutorials end at `vercel deploy`...

Publishing is git commit && git push. If you already have CI that builds and deploys on push, you are done — there is no second system to configure, no API token to rotate, no webhook to debug at eleven at night when a post did not appear.

The properties that follow from this are worth naming:

  • Posts are reviewable. A draft is a pull request. Someone can comment on line 40.
  • Content is versioned with the code that renders it. Change your callout component and the diff shows every post affected.
  • The site is fully static. No runtime dependency on a third party, and no cold-cache request that has to wait on someone else's API.
  • It costs nothing and has no quota.

And because MDX is Markdown plus JSX, you can drop a real interactive component into the middle of a post when the writing needs one. That is genuinely hard to replicate in a CMS.

What a CMS gets you

Everything MDX does not, once more than one person is involved:

  • A non-developer can publish without touching git.
  • Scheduled publishing, drafts, and an approval workflow.
  • Image uploads with automatic resizing and a CDN.
  • Structured content reused across surfaces — the same author bio on the blog, the about page, and a newsletter.
  • Editing from a phone.

None of these are nice-to-haves once you have hired someone whose job is content. They are the job.

The cost people underestimate

The catch with a CMS is not the CMS. It is what sits between the CMS and your deployed site.

Content lives somewhere else now, so your site has to find out when it changes. On managed hosting this is mostly solved — a webhook triggers revalidation and the page updates. On your own server it is more work: a webhook endpoint, a revalidation secret, and a cache that survives your deploy process. Add preview mode for unpublished drafts and you have a meaningful amount of infrastructure whose only job is moving text around.

That is a completely reasonable trade when someone is publishing three times a week. It is a strange trade when you publish monthly and the writer is you.

Start with MDX, keep the door open

The good news is that this is not a one-way decision, as long as you keep the seam clean.

Put every content read behind one module:

// lib/posts.ts
export function getAllPosts(): PostMeta[] { /* ... */ }
export function getPostBySlug(slug: string): Post | null { /* ... */ }
export function getAllSlugs(): string[] { /* ... */ }

Your pages import from there and know nothing about where posts come from. Today those functions read the filesystem. If you hire a content writer next year, they run a Sanity query instead — and the listing page, the post page, and the sitemap do not change at all.

The one thing to get right on day one is the shape of a post: title, slug, date, excerpt, category, cover image, author. That shape is your real schema, and it is what makes the swap mechanical rather than a rewrite. Validate it at build time — we use a Zod schema against the frontmatter, so a malformed post fails CI instead of rendering a blank card in production.

A reasonable default

For a small marketing site with a technical author, MDX in the repo is the right starting point. It is less code, less cost, and fewer moving parts, and it produces a faster site.

Revisit it the day someone who does not write code needs to publish. That day is a good problem to have, and if you kept the seam clean, it is an afternoon of work rather than a migration.

Keep reading

Server racks in a data centre
DevOps
4 min read

Deploying Next.js to a VPS with PM2 and Nginx

Vercel is not the only way to run Next.js. The full setup on a plain Linux box: process manager, reverse proxy, and a deploy on every push.

Anup Bhandari

Got a project in mind?

We build web applications and micro SaaS products. Tell us what you are working on.

Get in touch