Building a Modern Blog with Next.js and MDX
A comprehensive guide on creating a modern blog using Next.js 13+, MDX for content management, and Tailwind CSS for styling. Learn about SSR, routing, and best practices.
My Awesome Post
Here is the content of my post.
Building a Modern Blog with Next.js and MDX
Creating a modern blog requires balancing various factors: performance, SEO, developer experience, and user experience. In this comprehensive guide, I'll walk you through building a blog using Next.js 13+, MDX, and Tailwind CSS.
Why Next.js?
Next.js has become the go-to framework for React applications, and for good reason. It offers:
- Server-Side Rendering (SSR) and Static Site Generation (SSG)
- Automatic Route Pre-fetching
- Built-in Image Optimization
- API Routes
Let's dive into the implementation.
Project Setup
First, create a new Next.js project with TypeScript and Tailwind CSS:
npx create-next-app@latest my-blog --typescript --tailwind --eslint
cd my-blog
Directory Structure
Here's our project structure:
my-blog/
├── src/
│ ├── app/
│ │ ├── articles/
│ │ │ ├── [slug]/
│ │ │ │ └── page.tsx
│ │ │ └── page.tsx
│ │ └── layout.tsx
│ ├── components/
│ ├── content/
│ │ └── articles/
│ └── lib/
└── package.json
Implementing MDX Support
MDX allows us to use JSX in our markdown files. Here's how to set it up:
import { serialize } from 'next-mdx-remote/serialize';
import { MDXRemote } from 'next-mdx-remote/rsc';
import rehypeHighlight from 'rehype-highlight';
import remarkGfm from 'remark-gfm';
// Custom components for MDX
const mdxComponents = {
h1: (props: any) => (
<h1 className="text-4xl font-bold my-4" {...props} />
),
code: (props: any) => (
<code className="bg-gray-100 p-1 rounded" {...props} />
),
// ... more components
};
// Usage in page component
const mdxSource = await serialize(content, {
mdxOptions: {
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeHighlight],
},
});
Static Site Generation
Next.js 13+ introduces a new App Router with built-in support for static site generation:
// app/articles/[slug]/page.tsx
export async function generateStaticParams() {
const articles = getAllArticles();
return articles.map((article) => ({
slug: article.slug,
}));
}
export const dynamicParams = false;
Performance Optimizations
1. Image Optimization
Next.js provides the Image
component for automatic optimization:
import Image from 'next/image';
function BlogImage() {
return (
<Image
src="/blog-image.jpg"
alt="Blog Image"
width={800}
height={400}
priority
className="rounded-lg"
/>
);
}
2. Font Optimization
Use Next.js 13's built-in font optimization:
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.className}>
{children}
</html>
);
}
SEO Considerations
Next.js 13+ introduces a new metadata API:
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'My Blog',
description: 'A modern blog built with Next.js',
openGraph: {
title: 'My Blog',
description: 'A modern blog built with Next.js',
type: 'website',
},
};
Styling with Tailwind CSS
Tailwind CSS provides utility classes for rapid development. Here's an example of a card component:
function ArticleCard({ article }) {
return (
<div className="rounded-lg border border-gray-200 p-6 hover:shadow-lg transition-shadow">
<h2 className="text-2xl font-bold mb-2">{article.title}</h2>
<p className="text-gray-600">{article.description}</p>
<div className="mt-4 flex gap-2">
{article.tags.map((tag) => (
<span key={tag.slug} className="px-2 py-1 bg-gray-100 rounded-full text-sm">
{tag.name}
</span>
))}
</div>
</div>
);
}
Conclusion
Building a modern blog with Next.js offers numerous advantages:
- Excellent performance through static generation
- Great developer experience with TypeScript and MDX
- Built-in optimizations for images and fonts
- Strong SEO capabilities
- Beautiful styling with Tailwind CSS
The complete source code for this blog is available on GitHub.