Skip to content
Nick Diego

Migrating from WordPress to MDX

To start writing this post, I typed /new-post into Claude Code and answered a couple of questions. About 15 seconds later, I had a brand-new markdown file. MDX, to be specific, complete with frontmatter and ready to go.

This workflow was inspired by something Lee Robinson wrote recently:

The cost of abstractions with AI is very high.

He was describing Cursor’s decision to move their documentation from a headless CMS to MDX files, essentially trading a database-backed system for plain text. Once paired with agentic workflows, the benefits showed up almost immediately. It’s a great article and worth reading in full.

As many of you know, I’ve been building with WordPress for the better part of a decade. I still believe it’s the right choice for an enormous number of projects. But as I’ve adopted AI into my workflows, the abstraction layer WordPress provides has started to get in the way.

What I was fighting

In WordPress, content lives in a database. Media files land in wp-content/uploads. Styling and layout come from your theme, and custom functionality usually means plugins. Add the block editor on top, and while it’s excellent for visual editing, there’s a lot of machinery between you and the final output.

AI tools can’t easily “see” through that structure. When I ask Claude Code to help modify my blog, it can’t natively read my existing posts for context. When I want to add new functionality, it struggles to reference established design or development patterns across the site.

The WordPress AI team is actively working to change this, and several projects are already making real progress.

But for my personal site, where speed, iteration, and context matter more than traditional editorial workflows, it felt simpler to step back and rethink the foundation entirely. It also turned into a fun experiment.

The new setup

This entire site is now about 5,200 lines of code, excluding posts. It runs on Next.js 16. MDX handles content, Tailwind handles styling, and Shiki handles code blocks. Posts are just files:

src/blog/
├── 2025/
│   ├── simple-post.mdx
│   └── post-with-images/
│       ├── index.mdx
│       └── screenshot.png
└── 2026/
    └── another-post.mdx

Posts with images live in folders. Posts without images are a single file. Images sit right next to the content that uses them. There’s no media library and no attachment IDs.

I can drop custom components like <Image>, <Video>, or <GHRepoCard> directly into any post. Code blocks get syntax highlighting automatically. Light and dark mode just works.

Everything is version controlled in Git and deployed on Vercel. Content, components, and styling all live in the same place, which gives AI tools direct access to the full context of the site.

When I wanted to add copy buttons and expandable sections to code blocks, it was just a prompt. I then asked Claude to review all posts with code blocks and make any that were longer than 20 lines expandable. The whole process took maybe ten minutes.

Teaching Claude about the project

Like all Claude Code projects, this one has a CLAUDE.md file at the root. Beyond some general context, mine points to a small docs/ folder that explains how content works, what components exist, and the conventions the site follows. Things like styling rules, design patterns, and authoring guidelines live there. There’s still plenty more I could add, but even this amount goes a long way.

CLAUDE.md
docs/
├── components.md
├── content-authoring.md
├── design-patterns.md
├── getting-started.md
└── styling.md

I’ve also been leaning more heavily on “skills,” which is where this setup really starts to shine.

Skills are reusable prompts you invoke with a slash / command. I’ve built a few to automate the repetitive parts of writing and maintenance:

  • new-post prompts me with several questions about the post, then creates the correct file structure with frontmatter already filled in. I can also pass branch to work on a dedicated Git branch instead of using draft mode.

  • commit reviews the changes made in the current session, groups them logically, and writes commit messages. I can pass all to commit all pending changes and push to push when done.

I’m also experimenting with community skills. Things like web-design-guidelines review UI code against best practices, while vercel-react-best-practices help catch React performance issues. The ecosystem is still young, but it’s growing quickly. If you’re curious, Skills.sh is a good place to explore what’s out there.

Tradeoffs and takeaways

There are gaps. I don’t have comments yet, and probably won’t. Search is just browser find. Publishing happens when I merge a PR to main. That’s fine for a personal site, but it wouldn’t work everywhere. Some parts are likely overengineered, others too simple. This is still very much an experiment.

It’s also not the only way to approach this. There are excellent purpose-built tools like Astro that solve many of these problems out of the box. For me, this migration was an opportunity to build something from scratch, understand the tradeoffs firsthand, and explore what an AI-assisted workflow can look like.

The migration itself took a weekend. Another week of sporadic tinkering went into design tweaks and small features. Now I have a site that’s fast, cheap to run, easy to iterate on, and aligned with how I think and work.

The entire site is open source. You can browse the code, see how the skills work, and take whatever’s useful. Fair warning: this setup is experimental and changes often. If you’re exploring something similar, I’d love to hear about it. Reach out at @nickmdiego.