Create and manage static blog content using MDX files with React components.
Developer-Friendly: The MDX blog system is perfect for technical documentation, tutorials, and content that benefits from interactive React components.
The MDX blog uses a simple file-based structure:
src/app/[locale]/(public)/blog/mdx/
├── _files/
│ ├── introduction-next-js.mdx
│ ├── tailwind-css-v4.mdx
│ ├── guide-react-19.mdc
│ └── sample.mdx
├── [slug]/
│ └── page.tsx
└── page.tsx
Key Locations:
_files/ directory contains all MDX content[slug] route renders individual postspage.tsx displays list of all MDX postsCreate a new .mdx or .mdc file in the _files/ directory:
# Navigate to the MDX files directory
cd src/app/[locale]/(public)/blog/mdx/_files/
# Create a new blog post
touch my-awesome-post.mdxStart your MDX file with YAML frontmatter at the top of the file.
Frontmatter Fields:
title - Post title (required)description - Meta description for SEOpublishedAt - Publication datereadTime - Estimated reading timecategory - Content categorytags - Array of tagsauthor - Content authorAdd your content using Markdown and React components. You can use:
The MDX blog supports all documentation components:
<Callout type="info">
Information callout with blue styling
</Callout>
<Callout type="warning">
Warning callout with yellow styling
</Callout>
<Callout type="error">
Error callout with red styling
</Callout>
<Callout type="success">
Success callout with green styling
</Callout>Use descriptive, URL-friendly filenames:
# Good examples
introduction-to-react.mdx
nextjs-performance-tips.mdx
typescript-best-practices.mdx
# Avoid
My Post!.mdx
post-1.mdx
untitled.mdxOrganize content logically:
Use consistent categories in frontmatter:
"Development" - Technical content"Tutorial" - Step-by-step guides"News" - Updates and announcements"Tips" - Quick tips and tricksUse specific, searchable tags like:
["nextjs", "react", "typescript", "performance"]
Use consistent date format: "2024-01-15" (YYYY-MM-DD format)
# Create new MDX file
cd src/app/[locale]/(public)/blog/mdx/_files/
touch new-post.mdx
# Add frontmatter and content
# Save the file# Start development server
pnpm dev
# Navigate to MDX blog
open http://localhost:3000/blog/mdx
# Click on your new post to previewEnsure your React components work correctly:
# Test interactive components
<Button onClick={() => console.log('Clicked!')}>
Test Button
</Button>
# Test data display
<Tabs defaultValue="test">
<Tab value="test" label="Test">
This is a test tab
</Tab>
</Tabs>
# Add to git
git add .
git commit -m "Add new blog post: Getting Started with Next.js"
git push origin main
# Deploy automatically triggersMDX posts automatically generate SEO metadata:
Generated from frontmatter:
// Automatic generation
<meta name="title" content="Getting Started with Next.js 15" />
<meta name="description" content="Complete guide to the latest Next.js features" />
<meta property="og:title" content="Getting Started with Next.js 15" />
<meta property="og:description" content="Complete guide to..." />
<meta name="twitter:title" content="Getting Started with Next.js 15" />JSON-LD structured data for articles:
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Getting Started with Next.js 15",
"description": "Complete guide to the latest Next.js features",
"datePublished": "2024-01-15",
"author": {
"@type": "Person",
"name": "Tech Team"
}
}The MDX blog is optimized for performance:
Create custom components for your MDX content:
// components/mdx/demo-component.tsx
interface DemoProps {
title: string
children: React.ReactNode
}
export function Demo({ title, children }: DemoProps) {
return (
<div className="p-4 border rounded-lg bg-muted">
<h3 className="font-semibold mb-2">{title}</h3>
{children}
</div>
)
}import { Demo } from '@/components/mdx/demo-component'
# My Blog Post
<Demo title="Interactive Demo">
This is a custom component with interactive content.
</Demo>
Syntax highlighting is built-in:
```typescript title="user-service.ts" {2,5-7}
export interface User {
id: string // highlighted line
name: string
email: string
createdAt: Date // highlighted lines
updatedAt: Date // highlighted lines
isActive: boolean // highlighted lines
}
```
Convert database posts to MDX:
// Export database post to MDX
export async function exportToMDX(postId: string) {
const post = await getPostById(postId)
const frontmatter = {
title: post.title,
description: post.description,
publishedAt: post.publishedAt.toISOString().split('T')[0],
category: post.category?.name,
tags: post.hashtags.map(h => h.name)
}
const mdxContent = `---
${yaml.stringify(frontmatter)}---
${post.content}
`
fs.writeFileSync(`_files/${post.slug}.mdx`, mdxContent)
}Convert MDX files to database posts (see Database Blog documentation).
MDX Blog Ready! Your static blog system is now set up. Create .mdx files in the _files/ directory and they'll automatically appear on your blog at /blog/mdx.