Frontmatter support in MDX
@next/mdx does not support frontmatter by default, but frontmatter can be added using third-party packages like remark-frontmatter, remark-mdx-frontmatter, or gray-matter. Alternatively, use JavaScript exports in MDX files to define metadata (e.g., export const metadata = { author: 'John Doe' }), which can be imported and referenced outside the MDX file.
MDX packages to install for Next.js
Install @next/mdx, @mdx-js/loader, @mdx-js/react, and @types/mdx to render MDX with Next.js. These packages enable Next.js to process markdown and MDX files by sourcing data from local files, allowing creation of pages with .md or .mdx extension directly in the /pages or /app directory.
Configure next.config.mjs for MDX
Update next.config.mjs to configure MDX support. Set pageExtensions to include 'md' and 'mdx' alongside 'js', 'jsx', 'ts', 'tsx'. Import createMDX from '@next/mdx', call it with optional markdown plugins, and export the result wrapped around nextConfig. This allows .mdx files to act as pages, routes, or imports.
Handle .md files in MDX configuration
By default, @next/mdx only compiles files with .mdx extension. To handle .md files with webpack, update the extension option in createMDX to /\.(md|mdx)$/.
mdx-components.tsx file requirement
Create an mdx-components.tsx (or .js) file in the project root at the same level as pages or app, or inside src if applicable. This file must export a useMDXComponents function that returns an MDXComponents object. The file is required to use @next/mdx with App Router and will not work without it.
MDX file-based routing in App Router
Create MDX pages by adding .mdx or .md files in the /app directory using the file-based routing convention, such as app/mdx-page/page.mdx. These pages can use metadata, import React components, and markdown syntax directly. Navigating to the route will display the rendered MDX page.
Import MDX files as components
MDX files can be imported as React components and rendered within page files. Import the MDX file (e.g., import Welcome from '@/markdown/welcome.mdx'), then render it as a component in the page (return <Welcome />). This allows separating MDX content from the routing structure.
Dynamic MDX imports with dynamic route segments
In App Router, use dynamic route segments with async imports to load MDX components from a directory. Use await import(`@/content/${slug}.mdx`) to import files dynamically. Use generateStaticParams to prerender specific routes and set dynamicParams to false to return 404 for undefined routes. Always specify the .mdx extension in the import path.
Global MDX components in mdx-components.tsx
Define custom components in mdx-components.tsx that map to HTML elements generated by markdown. For example, customize h1, img, or other elements by providing them as properties in the components object. These global components affect all MDX files in the application. Components can include inline styles, components from other libraries, and custom React components.
Local MDX component overrides
Pass a components prop to imported MDX components to apply local styles and component overrides. Define custom component functions in the page file (e.g., CustomH1), add them to an overrideComponents object, and pass this object as the components prop to the MDX component (<Welcome components={overrideComponents} />). Local overrides merge with and override global styles and components.
Shared layouts for MDX pages in App Router
Use built-in layout support in App Router to share layouts across MDX pages. Create a layout.tsx file in the same directory as MDX pages (e.g., app/mdx-page/layout.tsx) that wraps children with shared styling or structure.
Metadata exports in MDX files
Define metadata as JavaScript exports in MDX files using export const metadata = { ... }. Import both the default component and named exports from the MDX file (e.g., import BlogPost, { metadata } from '@/content/blog-post.mdx'). The metadata can then be accessed as a JavaScript object outside the MDX file.
remark and rehype plugins for MDX
Pass remark and rehype plugins to @next/mdx by including them in the options object when calling createMDX. For example, use remark-gfm to support GitHub Flavored Markdown. Plugins are added via remarkPlugins and rehypePlugins arrays in the options. The remark and rehype ecosystem is ESM only, so next.config.mjs or next.config.ts must be used.
MDX plugins with Turbopack configuration
To use plugins with Turbopack, specify plugin names as strings instead of importing them directly. Plugins can be specified without options (e.g., 'remark-gfm') or with options as an array (e.g., ['remark-toc', { heading: 'The Table' }]). JavaScript functions cannot be passed to Rust, so non-serializable options are not supported with Turbopack.
Rust-based MDX compiler (experimental)
Next.js supports an experimental Rust-based MDX compiler. Enable it by setting experimental.mdxRs to true in next.config.js when passing to withMDX. This compiler is still experimental and not recommended for production use. The mdxRs option also accepts an object with properties: jsxRuntime (custom jsx runtime), jsxImportSource (custom jsx import source), providerImportSource (module providing useMDXComponents context), and mdxType ('gfm' or 'commonmark' for syntax configuration).
Markdown to HTML transformation process
Markdown is transformed to HTML using remark and rehype tools. The process involves: remark-parse converts markdown into a markdown AST, remark-rehype transforms the AST to HTML AST, rehype-sanitize sanitizes HTML input, and rehype-stringify converts the AST into serialized HTML. The @next/mdx package handles this transformation internally; direct use of remark and rehype is not required but may be needed for custom transformations.
Tailwind typography plugin for MDX styling
Use the @tailwindcss/typography plugin to style markdown content. Install the plugin and use it with shared layouts to add Tailwind prose classes that apply typographic styles to markdown-generated content blocks. Example: apply className="prose prose-headings:mt-8 prose-headings:font-semibold" to the wrapper div in a layout component.