Building Interactive MDX Components for Your Blog
Learn how to create engaging, interactive components that enhance your MDX blog posts and provide a better experience for your readers.
Taking Your Blog Beyond Static Content
One of the most powerful features of MDX is the ability to include interactive React components directly in your markdown content. This allows you to create rich, engaging experiences that wouldn't be possible with traditional markdown.
Simple Interactive Demo
Here's a basic example of an interactive counter component:
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return (
<div className="p-4 border rounded-md shadow-sm">
<p className="text-xl font-bold">Count: {count}</p>
<div className="flex gap-2 mt-2">
<button
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
onClick={() => setCount(count + 1)}
>
Increment
</button>
<button
className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
onClick={() => setCount(0)}
>
Reset
</button>
</div>
</div>
)
}Creating an Interactive Code Playground
You can even create components that allow readers to experiment with code directly in your blog:
export function CodePlayground({ initialCode, language }) {
const [code, setCode] = useState(initialCode)
const [output, setOutput] = useState('')
const runCode = () => {
try {
// Note: In a real implementation, you'd need to use a secure
// way to evaluate code or a sandbox environment
const result = eval(code)
setOutput(String(result))
} catch (error) {
setOutput(`Error: ${error.message}`)
}
}
return (
<div className="border rounded-md overflow-hidden my-4">
<div className="bg-gray-100 dark:bg-gray-800 p-2">
<textarea
className="w-full p-2 font-mono text-sm bg-white dark:bg-gray-900 rounded"
rows={5}
value={code}
onChange={(e) => setCode(e.target.value)}
/>
<button
className="mt-2 px-4 py-2 bg-green-500 text-white rounded"
onClick={runCode}
>
Run Code
</button>
</div>
<div className="p-4 bg-black text-white font-mono">
<div>Output:</div>
<pre>{output}</pre>
</div>
</div>
)
}Data Visualization Components
MDX is perfect for technical blogs that need to visualize data:
import { Bar } from 'react-chartjs-2'
export function TechUsageChart() {
const data = {
labels: ['React', 'Vue', 'Angular', 'Svelte', 'Remix'],
datasets: [
{
label: 'Developer Usage 2023',
data: [70, 45, 40, 30, 25],
backgroundColor: [
'rgba(54, 162, 235, 0.6)',
'rgba(75, 192, 192, 0.6)',
'rgba(255, 99, 132, 0.6)',
'rgba(255, 159, 64, 0.6)',
'rgba(153, 102, 255, 0.6)',
],
},
],
}
return (
<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow my-6">
<h3 className="text-lg font-bold mb-4">Framework Popularity</h3>
<Bar data={data} />
</div>
)
}Interactive Image Galleries
Blog posts about visual topics can benefit from interactive image viewers:
export function ImageGallery({ images }) {
const [activeIndex, setActiveIndex] = useState(0)
return (
<div className="my-6">
<div className="aspect-w-16 aspect-h-9 bg-gray-100 rounded-lg overflow-hidden">
<img
src={images[activeIndex].url}
alt={images[activeIndex].alt}
className="object-cover w-full h-full"
/>
</div>
<div className="flex mt-2 gap-2 overflow-x-auto py-2">
{images.map((image, idx) => (
<button
key={idx}
onClick={() => setActiveIndex(idx)}
className={`w-16 h-16 rounded overflow-hidden flex-shrink-0 border-2
${idx === activeIndex ? 'border-blue-500' : 'border-transparent'}`}
>
<img
src={image.url}
alt=""
className="object-cover w-full h-full"
/>
</button>
))}
</div>
</div>
)
}Making Your Components Available
Remember to make your components available to MDX by passing them in your MDX provider:
import { MDXProvider } from '@mdx-js/react'
import {
CodePlayground,
Counter,
ImageGallery,
TechUsageChart,
} from './components'
const components = {
Counter,
CodePlayground,
TechUsageChart,
ImageGallery,
}
export default function BlogPost({ children }) {
return (
<MDXProvider components={components}>
<article>{children}</article>
</MDXProvider>
)
}By integrating interactive components into your MDX blog posts, you can create a much more engaging and educational experience for your readers!