Next.js
Integrate MDX UI with a Next.js project using @next/mdx or next-mdx-remote
Overview
Next.js has two common ways to render MDX — pick the one that fits your project:
@next/mdx | next-mdx-remote | |
|---|---|---|
| MDX files as pages | Yes | No |
| Content from files / CMS | With extra setup | Yes |
| Frontmatter | Needs extra plugin | Built-in |
| Setup | Minimal | Minimal |
Use @next/mdx when your .mdx files live in app/ and act as pages directly.
Use next-mdx-remote when you load MDX from a content/ folder, a CMS, or a database.
1. Create a Next.js Project
Skip this step if you already have a project.
2. Initialize MDX UI
bash pnpm dlx @ravikumarsurya/mdx-ui init
This creates mdx-ui.json and copies the cn() utility into your project.
3. Add Components
bash pnpm dlx @ravikumarsurya/mdx-ui add callout accordion code-block
The CLI copies the component file and patches
components/mdx-ui/mdx-components.tsx with the correct imports and mappings
automatically.
4. Configure MDX
5. Render MDX Content
Create your MDX files in a content/ folder:
---
title: Hello World
date: 2024-01-01
---
# Hello World
<Callout variant="info" title="Welcome">
This page is rendered from a `.mdx` file in `content/`.
</Callout>Create app/blog/[slug]/page.tsx:
import fs from "fs";
import path from "path";
import matter from "gray-matter";
import { MDXRemote } from "next-mdx-remote/rsc";
import { mdxComponents } from "@/components/mdx-ui/mdx-components";
export async function generateStaticParams() {
const files = fs.readdirSync(path.join(process.cwd(), "content/blog"));
return files.map((f) => ({ slug: f.replace(/\.mdx?$/, "") }));
}
async function getPost(slug: string) {
const filePath = path.join(process.cwd(), "content/blog", `${slug}.mdx`);
const raw = fs.readFileSync(filePath, "utf8");
const { content, data } = matter(raw);
return { content, frontmatter: data };
}
export default async function BlogPost({
params,
}: {
params: { slug: string };
}) {
const { content, frontmatter } = await getPost(params.slug);
return (
<article className="max-w-3xl mx-auto px-6 py-10">
<h1 className="text-3xl font-bold mb-2">{frontmatter.title}</h1>
<MDXRemote source={content} components={mdxComponents} />
</article>
);
}Install gray-matter for frontmatter parsing:
bash pnpm add gray-matter Project Structure
Math Components
Math uses pure JSX primitives — no KaTeX or LaTeX required:
See Math Primitives for the full list.
Troubleshooting
Components not rendering in MDX
Make sure mdxComponents is passed to your renderer:
- next-mdx-remote:
<MDXRemote source={content} components={mdxComponents} /> - @next/mdx: The root
mdx-components.tsxwithuseMDXComponentswires this up automatically.
Cannot find module @/components/mdx-ui/...
Add the @/ path alias to tsconfig.json:
{
"compilerOptions": {
"paths": {
"@/*": ["./*"]
}
}
}TypeScript error on .mdx imports (@next/mdx only)
Add to next-env.d.ts or a .d.ts file:
declare module "*.mdx" {
import type { ComponentType } from "react";
const Component: ComponentType;
export default Component;
}Tailwind classes not applying
Make sure your globals.css (Tailwind v4) or tailwind.config.ts (Tailwind v3) includes the component paths:
export default {
content: [
"./app/**/*.{ts,tsx,mdx}",
"./components/**/*.{ts,tsx}",
"./content/**/*.mdx",
],
};Next Steps
On This Page