Markdown is a lightweight markup language used to format text. It allows you to write using plain text syntax and convert it to structurally valid HTML. It's commonly used for writing content on websites and blogs.
// You write
I **love** using [Next.js](https://nextjs.org/)
// Output HTML
<p>
I <strong>love</strong> using <a href="https://nextjs.org/">Next.js</a>
</p>
React does not natively understand Markdown. The markdown plaintext needs to first be transformed into HTML. This can be accomplished with remark
and rehype
.
remark
is an ecosystem of tools around markdown. rehype
is the same, but for HTML. For example, the following code snippet transforms markdown into HTML:
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeSanitize from 'rehype-sanitize';
import rehypeStringify from 'rehype-stringify';
main();
async function main() {
const file = await unified()
.use(remarkParse) // Convert into markdown AST
.use(remarkRehype) // Transform to HTML AST
.use(rehypeSanitize) // Sanitize HTML input
.use(rehypeStringify) // Convert AST into serialized HTML
.process('# Hello, Next.js!');
console.log(String(file)); // <h1>Hello, Next.js!</h1>
}
The remark
and rehype
ecosystem contains plugins for syntax highlighting, linking headings, generating a table of contents, and more.
When using @next/mdx
as shown below, you do not need to use remark
or rehype
directly, as it is handled for you.
MDX is a superset of markdown that lets you write JSX directly in your markdown files. It is a powerful way to add dynamic interactivity and embed React components within your content.
Next.js can support both local MDX content inside your application, as well as remote MDX files fetched dynamically on the server. The Next.js plugin handles tranforming Markdown and React components into HTML, including support for usage in Server Components (default in app
).
To use Markdown and MDX in Next.js:
@next/mdx
package:npm install @next/mdx
mdx-components.jsx
in the root of your application (not inside app
):// This file allows you to provide custom React components
// to be used in MDX files. You can import and use any
// React component you want, including components from
// other libraries.
function H1({ children }) {
// ...
}
function H2({ children }) {
// ...
}
export function useMDXComponents(components) {
return { h1: H1, h2: H2, ...components };
}
next.config.js
to use @next/mdx
:/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
mdxRs: true,
},
}
const withMDX = require('@next/mdx')()
module.exports = withMDX(nextConfig)
app
directory:// You can import and use React components
// import AnotherComponent from './component';
# Hello, Next.js!
Lorem ipsum dolor sit amet.
// <AnotherComponent />
page
to display the content:import HelloWorld from './hello.mdx';
export default function Page() {
return <HelloWorld />;
}
You can also use the following example which includes these steps.
If your Markdown or MDX files do not live inside your application, you can fetch them dynamically on the server. This is useful for fetching content from a CMS or other data source.
There are two popular community packages for fetching MDX content: next-mdx-remote
and contentlayer
. For example, the following example uses next-mdx-remote
:
Note: Please proceed with caution. MDX compiles to JavaScript and is executed on the server. You should only fetch MDX content from a trusted source, otherwise this can lead to remote code execution (RCE).
import { MDXRemote } from 'next-mdx-remote/rsc'
export default async function Home() {
const res = await fetch('https://...')
const markdown = await res.text()
return <MDXRemote source={markdown} />;
}
To share a layout around MDX content, you can use the built-in layouts support.
You can optionally provide remark
and rehype
plugins to transform the MDX content. For example, you can use remark-gfm
to support GitHub Flavored Markdown.
Since the remark
and rehype
ecosystem is ESM only, you'll need to use next.config.mjs
as the configuration file.
import addMdx from '@next/mdx';
addMdx(nextConfig, {
options: {
remarkPlugins: [],
rehypePlugins: [],
// If you use `MDXProvider`, uncomment the following line.
// providerImportSource: "@mdx-js/react",
}
})
export default {
pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'mdx'],
experimental: {
appDir: true,
mdxRs: true,
}
}