Building a Modern, Interactive Blog with Next.js, MDX and Tailwind CSS

Last updated: January 1, 2026

To kick off the New Year 2026, I decided to upgrade my old GitHub Pages blog with modern tools. I’m excited to start sharing new posts about my learning journey and insights along the way.

In this post, I’ll walk you through how I built a feature-rich, interactive blog using Next.js, MDX and Tailwind CSS, complete with syntax highlighting, SEO optimization, and a sleek design.

Why This Tech Stack?

The combination of Next.js, MDX and Tailwind CSS provides a powerful and flexible foundation for modern blogging:

  • Next.js — Static site generation and server-side rendering for excellent performance and SEO
  • MDX — Write content in Markdown while embedding React components seamlessly
  • Tailwind CSS — Utility-first styling for rapid development and consistent design

This stack gives you the simplicity of Markdown with the flexibility of React and the performance benefits of a modern framework.

Setting Up the Project

Start by creating a new Next.js project:

1npx create-next-app@latest my-blog
2cd my-blog

Install the dependencies needed for MDX and syntax highlighting:

1npm install next-mdx-remote gray-matter react-syntax-highlighter
2npm install -D @types/react-syntax-highlighter

Configuring MDX

Update your next.config.mjs to support MDX:

1import createMDX from '@next/mdx';
2
3const withMDX = createMDX({
4  extension: /\.mdx?$/,
5  options: {
6    remarkPlugins: [],
7    rehypePlugins: [],
8  },
9});
10
11const nextConfig = {
12  output: 'export',
13  images: { unoptimized: true },
14  pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'mdx'],
15};
16
17export default withMDX(nextConfig);

This configuration allows Next.js to treat .mdx files as first-class pages.

Creating a Dynamic Blog Post Page

Here’s a simple example of a dynamic blog post page that renders MDX content:

1import { getPostBySlug, getAllPosts } from '@/lib/posts';
2import { MDXRemote } from 'next-mdx-remote/rsc';
3
4export async function generateStaticParams() {
5  const posts = getAllPosts();
6  return posts.map((post) => ({
7    slug: post.slug,
8  }));
9}
10
11export default async function PostPage({
12  params,
13}: {
14  params: Promise<{ slug: string }>;
15}) {
16  const { slug } = await params;
17  const { content, data } = getPostBySlug(slug);
18
19  return (
20    <article className="prose mx-auto p-6">
21      <h1>{data.title}</h1>
22      <MDXRemote source={content} />
23    </article>
24  );
25}

This setup enables static generation of each blog post while keeping the content flexible and easy to manage.

Adding Syntax Highlighting (Dark & Light Mode)

To render beautiful code blocks with theme support, create a reusable CodeBlock component:

1'use client';
2
3import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
4import {
5  oneDark,
6  oneLight,
7} from 'react-syntax-highlighter/dist/esm/styles/prism';
8import { useTheme } from 'next-themes';
9
10interface CodeBlockProps {
11  children?: string | string[];
12  language?: string;
13}
14
15export default function CodeBlock({ children, language }: CodeBlockProps) {
16  const { theme } = useTheme();
17  const isDark = theme === 'dark';
18
19  return (
20    <SyntaxHighlighter
21      language={language}
22      style={isDark ? oneDark : oneLight}
23      showLineNumbers
24    >
25      {String(children || '')}
26    </SyntaxHighlighter>
27  );
28}

You can now use this component inside your MDX files for clean, readable code snippets.

Styling with Tailwind CSS

Tailwind makes it easy to build clean, responsive layouts:

1export default function BlogPost({ post }) {
2  return (
3    <div className="max-w-4xl mx-auto px-4 py-8">
4      <article className="prose prose-lg max-w-none">
5        <h1 className="text-4xl font-bold mb-4">{post.title}</h1>
6        <p className="text-gray-600 dark:text-gray-400 mb-8">
7          {post.description}
8        </p>
9        <MDXRemote source={post.content} />
10      </article>
11    </div>
12  );
13}

SEO Optimization with Metadata

Adding metadata improves search visibility and social sharing:

1import { Metadata } from 'next';
2
3export const metadata: Metadata = {
4  title: 'My Blog — Modern Web Development',
5  description: 'A modern blog built with Next.js, MDX, and Tailwind CSS',
6  keywords: ['Next.js', 'MDX', 'Tailwind CSS', 'Blog', 'Web Development'],
7  authors: [{ name: 'Your Name' }],
8  openGraph: {
9    title: 'My Blog — Modern Web Development',
10    description: 'A modern blog built with Next.js, MDX, and Tailwind CSS',
11    type: 'website',
12    locale: 'en_US',
13  },
14};

MDX Blog Posts

MDX gives you the flexibility of React with the simplicity of Markdown — perfect for technical blogging and documentation.

Example MDX Blog Post: (posts/hello-world.mdx)

1---
2title: Hello World
3description: My first post written in MDX
4date: 2026-01-01
5---
6
7Welcome to my blog 👋  
8This is my first post written using **MDX**, which allows me to combine Markdown with React components.
9
10## Why MDX?
11
12MDX lets you:
13
14- Write content using Markdown
15- Embed React components directly in posts
16- Create interactive, reusable blog experiences
17
18## Sample Code Block
19
20Here’s a simple example using JavaScript:
21
22```js
23function greet(name) {
24  return `Hello, ${name}!`;
25}
26
27console.log(greet('World'));
28```

Conclusion

This setup provides a modern blog with:

  • ✅ Syntax highlighting for code blocks
  • ✅ Responsive design with Tailwind CSS
  • ✅ Static site generation for GitHub Pages
  • ✅ SEO optimization with metadata
  • ✅ MDX support for flexible content

I’m excited to keep blogging and sharing what I learn while building across Agentic AI, frontend engineering, and cloud-native systems. 🚀