typescriptintermediate
MDX Blog with Next.js
Set up MDX support for a blog with custom components, frontmatter, and syntax highlighting.
typescriptPress ⌘/Ctrl + Shift + C to copy
// next.config.mjs
import createMDX from '@next/mdx';
const withMDX = createMDX({
options: {
remarkPlugins: [],
rehypePlugins: [],
},
});
export default withMDX({
pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
});
// mdx-components.tsx (root level)
import type { MDXComponents } from 'mdx/types';
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
h1: ({ children }) => (
<h1 className="text-3xl font-bold mt-8 mb-4">{children}</h1>
),
h2: ({ children }) => (
<h2 className="text-2xl font-semibold mt-6 mb-3">{children}</h2>
),
p: ({ children }) => (
<p className="text-gray-300 leading-relaxed mb-4">{children}</p>
),
code: ({ children }) => (
<code className="bg-gray-800 px-1.5 py-0.5 rounded text-sm">
{children}
</code>
),
pre: ({ children }) => (
<pre className="bg-gray-900 p-4 rounded-lg overflow-x-auto mb-4">
{children}
</pre>
),
a: ({ href, children }) => (
<a href={href} className="text-blue-400 hover:underline" target="_blank" rel="noopener">
{children}
</a>
),
...components,
};
}
// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation';
import { readdir } from 'fs/promises';
import path from 'path';
export async function generateStaticParams() {
const files = await readdir(path.join(process.cwd(), 'content/blog'));
return files
.filter((f) => f.endsWith('.mdx'))
.map((f) => ({ slug: f.replace('.mdx', '') }));
}
export default async function BlogPost({
params,
}: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
try {
const { default: Content, metadata } = await import(`@/content/blog/${slug}.mdx`);
return (
<article className="prose dark:prose-invert max-w-3xl mx-auto">
<h1>{metadata.title}</h1>
<Content />
</article>
);
} catch {
notFound();
}
}Use Cases
- technical blogs
- documentation
- content-heavy sites
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
typescriptadvanced
Draft Mode for CMS Preview
Enable draft mode to preview unpublished CMS content in production with cookie-based toggling.
Best for: CMS preview
#nextjs#draft-mode
typescriptintermediate
Dynamic Sitemap Generation
Generate a comprehensive sitemap.xml from dynamic routes and database content.
Best for: SEO optimization
#nextjs#sitemap
typescriptintermediate
Type-Safe API Route Handler
Next.js App Router route handler with input validation, typed responses, and proper error handling.
Best for: CRUD API endpoints
#api#route-handler
typescriptintermediate
Authentication Middleware Guard
Next.js middleware that checks auth tokens on protected routes and redirects unauthenticated users to login.
Best for: Dashboard access control
#middleware#authentication