Processing MDX Files
MDX brings JSX components to markdown, which can provide power and flexibility to the main body area of a content piece.
# Hello, World!
This is my first MDX file. Here's a button element <button>Click me!</button>.
<MyComponent />
MDX Content Type
Contentlayer supports MDX processing via mdx-bundler
. By default, Contentlayer processes the main content area of .md
and .mdx
files as markdown.
You can enable MDX processing by setting the contentType
option in your document type definition to 'mdx'
in your Contentlayer configuration.
// contentlayer.config.ts
const Post = defineDocumentType(() => ({
name: 'Post',
filePathPattern: `**/*.mdx`,
contentType: 'mdx',
fields: {
// ...
},
}))
Usage in Next.js
To parse the contents of a MDX file in a Next.js page, use the useMDXComponent
hook provided by next-contentlayer/hooks
.
Pages Directory
Here's an example implementation in Next.js using the legacy /pages
directory:
// pages/posts/[slug].tsx
import { allPosts, type Post } from 'contentlayer/generated'
import { useMDXComponent } from 'next-contentlayer/hooks'
export async function getStaticPaths() {
// Get a list of valid post paths.
const paths = allPosts.map((post) => ({
params: { slug: post._raw.flattenedPath },
}))
return { paths, fallback: false }
}
export async function getStaticProps(context) {
// Find the post for the current page.
const post = allPosts.find((post) => post._raw.flattenedPath === context.params.slug)
// Return notFound if the post does not exist.
if (!post) return { notFound: true }
// Return the post as page props.
return { props: { post } }
}
export default function Page({ post }: { post: Post }) {
// Parse the MDX file via the useMDXComponent hook.
const MDXContent = useMDXComponent(post.body.code)
return (
<div>
{/* Some code ... */}
<MDXContent />
</div>
)
}
In this example, the getStaticPaths
function returns an array of all valid post slugs for pre-rendering and the getStaticProps
function retrieves the relevant MDX post for the current page. The useMDXComponent
hook then processes the MDX file via mdx-bundler
. Finally, the rendered content is displayed using the MDXContent
component returned by the useMDXComponent
hook.
App Directory
Here's an example implementation in Next.js version 13 and above using the new /app
directory:
// app/posts/[slug]/page.tsx
import { allPosts } from 'contentlayer/generated'
import { useMDXComponent } from 'next-contentlayer/hooks'
import { notFound } from 'next/navigation'
export async function generateStaticParams() {
return allPosts.map((post) => ({
slug: post._raw.flattenedPath,
}))
}
export default async function Page({ params }: { params: { slug: string } }) {
// Find the post for the current page.
const post = allPosts.find((post) => post._raw.flattenedPath === params.slug)
// 404 if the post does not exist.
if (!post) notFound()
// Parse the MDX file via the useMDXComponent hook.
const MDXContent = useMDXComponent(post.body.code)
return (
<div>
{/* Some code ... */}
<MDXContent />
</div>
)
}
In this example, the generateStaticParams
function returns an array of all valid post slugs for pre-rendering. The useMDXComponent
hook then processes the MDX file for the current page via mdx-bundler
. Finally, the rendered content is displayed using the MDXContent
component returned by the useMDXComponent
hook.
Custom MDX Components
You can override built-in HTML elements and create your own custom React components using the components
prop of the MDXContent
component returned by the useMDXComponent
hook.
Here's an example implementation:
import { allPosts } from 'contentlayer/generated'
import type { MDXComponents } from 'mdx/types'
import { useMDXComponent } from 'next-contentlayer/hooks'
import Link from 'next/link'
// Define your custom MDX components.
const mdxComponents: MDXComponents = {
// Override the default <a> element to use the next/link component.
a: ({ href, children }) => <Link href={href as string}>{children}</Link>,
// Add a custom component.
MyComponent: () => <div>Hello World!</div>,
}
export default async function Page({ params }: { params: { slug: string } }) {
// Find the post for the current page.
const post = allPosts.find((post) => post._raw.flattenedPath === params.slug)
// 404 if the post does not exist.
if (!post) notFound()
// Parse the MDX file via the useMDXComponent hook.
const MDXContent = useMDXComponent(post.body.code)
return (
<div>
{/* Some code ... */}
<MDXContent components={mdxComponents} /> {/* <= Include your custom MDX components here */}
</div>
)
}
In this example, we define a custom mdxComponents
object that overrides the default <a>
element to use the next/link
component and adds a custom MyComponent
component. Then, we include our custom components by passing the mdxComponents
object to the components
prop of the MDXContent
component.
MDX Plugins (remark/rehype)
You can add remark and rehype plugins inside the mdx
property when calling makeSource
in your Contentlayer configuration.
// contentlayer.config.ts
import { makeSource } from '@contentlayer/source-files'
import highlight from 'rehype-highlight'
import remarkGfm from 'remark-gfm'
export default makeSource({
// ...
mdx: {
remarkPlugins: [remarkGfm],
rehypePlugins: [highlight],
},
})
Was this article helpful to you?
Provide feedback
Last edited on May 24, 2023.
Edit this page