Hello World: Building a Minimal Blog with MDX
Welcome to my new blog! I've been meaning to create a space to share my thoughts on code, design, and the intersection of both. After some deliberation, I decided to build a minimal blog system right into my portfolio site.
Why MDX?
MDX combines the simplicity of Markdown with the power of React components. This means I can write content in a familiar format while still being able to embed interactive components when needed.
// Example: A simple React component in MDX
const WaveHello = () => {
const [waves, setWaves] = useState(0);
return (
<button onClick={() => setWaves(waves + 1)}>
š Wave hello! (Waved {waves} times)
</button>
);
};
The Setup
The blog system is surprisingly simple. Here's what powers it:
- Next.js - For routing and server-side rendering
- MDX - For content authoring
- rehype-pretty-code - For syntax highlighting
- gray-matter - For frontmatter parsing
- reading-time - For estimated reading times
Code Structure
The blog follows a simple file-based routing pattern:
src/
āāā app/
ā āāā blog/
ā āāā page.tsx # Blog listing
ā āāā [slug]/
ā āāā page.tsx # Individual posts
āāā content/
ā āāā blog/
ā āāā *.mdx # Blog posts
āāā lib/
āāā blog.ts # Helper functions
What's Next?
This is just the beginning. I plan to write about:
- Deep dives into React patterns
- Performance optimization techniques
- Design system architecture
- Open source contributions
- Side project post-mortems
The Beauty of Simplicity
Sometimes the best solution is the simplest one. This blog doesn't have comments, likes, or analytics. It's just words on a page, styled consistently with the rest of my site. And that's exactly what I wanted.
// The entire blog post fetching logic
export function getPostBySlug(slug: string): BlogPost | null {
try {
const fullPath = path.join(postsDirectory, `${slug}.mdx`)
const fileContents = fs.readFileSync(fullPath, 'utf8')
const { data, content } = matter(fileContents)
const stats = readingTime(content)
return {
slug,
title: data.title,
date: data.date,
excerpt: data.excerpt || '',
tags: data.tags || [],
readingTime: stats.text,
content,
}
} catch {
return null
}
}
Stay tuned for more posts. I'm excited to share what I've been learning and building.