Thoughts on code,
craft & creativity

Deep dives into React, MDX, web performance, and the tools that make us better developers.

March 12, 2026 8 min read

Building Interactive Blog Posts with MDX and React

Learn how to create rich, interactive articles by combining the simplicity of Markdown with the power of React components. We'll build live code editors, animated diagrams, and explorable explanations.

React MDX
March 5, 2026 12 min read

The Surprising Truth About CSS Container Queries

Container queries change everything about responsive design. But the real magic isn't what you think it is. Let's explore the mental model shift that makes them truly powerful.

CSS Performance
Feb 28, 2026 6 min read

Type-Safe Markdown: A TypeScript Plugin for MDX

What if your MDX content could be fully type-checked at build time? We built a TypeScript plugin that validates frontmatter, component props, and even content structure.

TypeScript Tooling
Feb 20, 2026 10 min read

React Server Components Changed My Mind About SSR

I was skeptical. Another rendering paradigm? But after migrating a content-heavy site to RSC, the performance gains and developer experience improvements won me over completely.

React Performance
Feb 14, 2026 7 min read

Remark Plugins That Will Transform Your MDX Pipeline

The unified ecosystem is incredibly powerful but poorly documented. Here are seven remark and rehype plugins that supercharged our content pipeline, with practical examples.

MDX Tooling
Feb 7, 2026 9 min read

Crafting Fluid Animations with CSS View Transitions

The View Transitions API brings cinematic page transitions to the web. Let's build a blog with smooth, app-like navigation that works across routes with zero JavaScript animation libraries.

CSS React
March 12, 2026 8 min read React MDX

Building Interactive Blog Posts with MDX and React

How to create living documents that teach through exploration, not just explanation.

Markdown has been the backbone of developer content for over a decade. But static text can only go so far. What if your blog posts could respond to the reader? What if code examples could be edited live, and diagrams could animate to reveal complexity layer by layer?

That's the promise of MDX — a format that lets you seamlessly blend Markdown with JSX. And with the right architecture, you can build a blog engine that makes writing interactive content as natural as writing prose.

Why MDX Changes Everything

Traditional Markdown gives you a limited vocabulary: headings, paragraphs, lists, code blocks, images. It's deliberately simple, and that simplicity is its greatest strength. But when you're trying to explain complex concepts — state machines, rendering pipelines, data structures — you hit a wall.

The best technical writing doesn't just tell you how something works. It lets you see it working and feel the constraints that shaped its design.

MDX solves this by letting you import and use React components directly in your Markdown files. The syntax feels natural if you already know both Markdown and JSX:

mdx
---
title: Building with MDX
date: 2026-03-12
---

import { LiveEditor, AnimatedDiagram } from './components'

# Getting Started

Here's a regular paragraph with **bold** and *italic* text.
But now we can also drop in a React component:

<LiveEditor
  code={`const greeting = "Hello, MDX!"`}
  language="javascript"
/>

And continue writing Markdown seamlessly below it.

The Architecture Behind It

Building a proper MDX blog engine requires careful thought about several layers. Let's walk through the architecture that powers this site.

💡
This architecture works with Next.js, Remix, and Astro. The core concepts are framework-agnostic, though the integration points differ slightly.

The Content Layer

At the foundation, we need a way to discover, parse, and transform MDX files. The unified ecosystem gives us a powerful pipeline:

typescript
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkMdx from 'remark-mdx'
import remarkRehype from 'remark-rehype'
import rehypeHighlight from 'rehype-highlight'

interface BlogPost {
  slug: string
  title: string
  date: Date
  content: MDXContent
  readingTime: number
  toc: TableOfContents
}

async function compileMDX(source: string): Promise<BlogPost> {
  const processor = unified()
    .use(remarkParse)
    .use(remarkMdx)
    .use(remarkToc, { tight: true })
    .use(remarkReadingTime)
    .use(remarkRehype)
    .use(rehypeHighlight)

  return processor.process(source)
}

Component Mapping

The real power of MDX comes from mapping Markdown elements to custom React components. Instead of rendering a plain <pre> tag, you can render a full-featured code editor with syntax highlighting, line numbers, and a copy button:

tsx
const components = {
  // Map Markdown elements to custom components
  h2: ({ children }) => (
    <h2 id={slugify(children)}>
      <a href={`#${slugify(children)}`}>{children}</a>
    </h2>
  ),
  pre: ({ children }) => (
    <CodeBlock copyable lineNumbers>
      {children}
    </CodeBlock>
  ),
  img: ({ src, alt }) => (
    <OptimizedImage
      src={src}
      alt={alt}
      loading="lazy"
    />
  ),
}

Performance Considerations

Interactive content can bloat your bundle quickly. Here are the strategies we use to keep things fast:

  • Lazy-load interactive components — Don't ship the code editor JavaScript unless the reader scrolls to it.
  • Static extraction — Pre-render as much as possible at build time. Syntax highlighting, reading time, and table of contents should all be computed ahead.
  • Island architecture — Only hydrate the interactive parts. The surrounding prose stays as static HTML.
  • Bundle splitting — Each custom component gets its own chunk, loaded on demand.
⚠️
Watch your bundle size. A single poorly-optimized interactive component can add 50KB+ of JavaScript. Always measure with next build --analyze or equivalent.

Building Custom Components

The best MDX blogs have a library of reusable components designed for teaching. Here's our component for showing a before/after comparison:

tsx
function BeforeAfter({ before, after, labels }: Props) {
  const [position, setPosition] = useState(50)

  return (
    <div className="comparison-slider">
      <div className="panel before"
           style={{ width: `${position}%` }}>
        <span className="label">{labels?.[0] ?? 'Before'}</span>
        {before}
      </div>
      <input
        type="range"
        min={0} max={100}
        value={position}
        onChange={(e) => setPosition(+e.target.value)}
      />
      <div className="panel after">
        <span className="label">{labels?.[1] ?? 'After'}</span>
        {after}
      </div>
    </div>
  )
}

Then in your MDX file, using it is as simple as:

mdx
<BeforeAfter
  before={<CodeBlock>{oldCode}</CodeBlock>}
  after={<CodeBlock>{newCode}</CodeBlock>}
  labels={["Without MDX", "With MDX"]}
/>

What's Next

We've only scratched the surface. In upcoming posts, we'll explore:

  1. Building a live code playground with Sandpack integration
  2. Animated diagrams using Motion and MDX
  3. Content collections and how to manage hundreds of MDX files with type safety
  4. RSS generation from MDX with full content, not just excerpts

The key insight is that MDX isn't just "Markdown plus components" — it's a fundamentally different way of thinking about technical content. When your writing medium supports interactivity, you start designing explanations differently. You lean into showing rather than telling, and your readers learn faster because of it.

If you're building a developer blog in 2026, MDX with a well-designed component library isn't optional anymore. It's the baseline your readers expect.