Building a blog engine in MDX that marketing can run
MDX lets a post carry real components, but a marketer needs an editor, guardrails and previews. The trade-offs of MDX against a database CMS, from a rebuild we did.
- MDX lets a post carry components such as callouts, tables and diagrams.
- On its own it needs a developer. Add an editor and a shared component set for marketers.
- Keep the component set small, so posts stay consistent.
- A database CMS is simpler when no developer is available.
Quick answer
An MDX blog engine lets a post carry real components, such as callouts, tables, diagrams and calls to action, while staying plain text underneath. Marketing can run it if you give them a fixed set of components, a frontmatter template, a safe fallback when a post fails to compile, a dry-run before anything publishes, and an editor that never asks them to touch code.
The short answer
MDX is Markdown that can embed components. That’s the whole idea, and it’s why it’s good and why it’s risky. Good, because a post can have a proper callout, a comparison table or an inline diagram without anyone building a page. Risky, because one mistyped tag can stop a post compiling.
MDX is for developers unless you build the editor around the marketing team. The engine is the easy part. The guardrails are the product.
Why buyers ask this
Marketing wants a blog that looks designed, not a wall of text, and wants to publish without filing a ticket. Developers worry about two things: a messy mix of formats, and a post that breaks the page. MDX can answer both, if you decide a few things up front.
What an MDX engine is made of
| Layer | Job | The decision that matters |
|---|---|---|
| Content | Posts as text with a metadata block (frontmatter) on top | A fixed set of fields, so every post is shaped the same |
| Components | The things a post can embed | A short, named list. Not “any React component” |
| Compiler | Turns a post into a page | What happens when it fails |
| Store | Where posts live: files, a database or both | Who edits what, and how changes are reviewed |
| Editor | Where the team writes | No raw component syntax for routine work |
| Output | The page, the sitemap, structured data, internal links | Generated from the same fields |
Frontmatter is a contract
The metadata block at the top of each post is what turns a file into a managed asset. Ours carries the post’s type and status, slug, title, topic, a one-sentence deck, a quick answer that stands alone, key takeaways, the claims it relies on, a last-verified date, SEO title and description, target prompts, FAQs and related posts. Case studies add the industry, services, tech stack, a summary of the challenge, the solution and the result, and a note on what was and wasn’t measured.
Two rules make the contract useful:
- Required fields fail loudly. A post with no slug or title shouldn’t load.
- Defaults are safe. An optional field that’s empty falls back to something harmless, never to a broken page.
A small, fixed component set
Give the team five or six components and no more. Ours are:
- QuickAnswer. A standalone answer at the top. Good for readers and for AI tools that quote it.
- CalloutBox. For proof grades, warnings and “what this doesn’t prove”.
- InlineCTA. A call to action with a headline and a button, in a few visual styles.
- Accordion. For sources and the long tail of detail.
- Diagram. Draws a flow from a few plain lines of text, such as
A -> B, so nobody opens a design tool. - Tables. Plain Markdown tables, styled once.
A component list is also an editorial policy. If every post ends with an InlineCTA and a sources note, readers learn the shape. If anyone can embed anything, nobody does.
Compile safely
MDX fails on a stray bracket. Assume it will. A page that renders a 500 error because of a typo is the fastest way to lose the team’s trust.
- Compile when you load, not when a reader visits. A bad post is caught before it can go live.
- Fall back. If compilation fails at render time, show the post as plain content and log the error. Ours does exactly this.
- Dry-run first. Our loader reads every file and reports what it would do. Nothing is written until you add a flag. A second flag publishes, and without it posts land as drafts.
- Check the frontmatter parses. Unescaped quotes inside a quoted field are the commonest culprit. A YAML error can stop a whole batch.
Files, database or both
| Approach | Good for | Cost |
|---|---|---|
| Files in the repository | Developers, review in pull requests, a full history | Marketing needs a developer to merge |
| A database with an editor | Marketing publishes alone, roles and workflow | Content is no longer in version control |
| Files loaded into a database | Both: write and review as files, publish through the system | Two places to keep in sync |
We use the third. Posts are written as files, checked in a dry run, loaded as drafts and published from the admin by someone with the right role. That gives developers review and marketing a publish button. It’s more machinery, and worth it when the team is both.
Make the claims checkable
The best use we’ve found for frontmatter is a claims field. Every factual statement a post makes has an ID in a ledger that records its source, the date it was checked and a proof grade. A claim that isn’t in the ledger doesn’t go on a page. It slows the first draft and removes most of the arguments afterwards, because every number has an address.
What marketing needs from the editor
- A place to write that never shows raw component syntax for everyday work.
- A preview that matches the live page.
- Autosave and a way to restore an earlier version.
- A review step and a publish gate, so a draft can’t go live by accident.
- Clear error messages. “Line 42: unclosed tag” beats “build failed”.
- The ability to import from a word processor, with clean-up at the door. Pasted documents bring odd quotation marks and invisible characters.
The pitfalls
- Too many components. Every one is another way for a post to break.
- Content that depends on a component’s internals. Change the component, break a hundred posts.
- No fallback, so a typo becomes an outage.
- Hand-built sitemaps and structured data. Generate them.
- No dry-run. The first time you see an error shouldn’t be in production.
- Frontmatter that varies post to post. You can’t build navigation on that.
We run this engine ourselves, and we’ve built a blog on MDX for a client too. We have no measured effect on traffic or publishing speed to quote. If you publish a few posts a month and one person does it, a hosted blog platform is simpler. MDX earns its keep when posts need real components and several people publish.
Next: what a CMS needs so editors publish without breaking SEO and can marketing publish without a developer.
The engine description, frontmatter fields, component list, dry-run and draft behaviour come from the GarunaCDX resource-hub content loader and components in our own repository, and the claim-ledger rule from our hub README, all read on 26 October 2026. The client-blog mention comes from our project notes and is marked for confirmation in our claim ledger. Proof grade 4: our own build.
More by GarunaCDX
Let's talk about your build.
Tell us what you are building. A founding engineer replies within one working day with how we would approach it.