MDX bundler with Next.JS
For my personal blog, I have to choose a MDX file in order to display rich content or embed custom react components in markdown. Before going further how to use MDX with Next.JS, I would like to explain why I select MDX and mdx-bundler to compile my markdown files.
The source code for this tutorial can be found on GitHub.
Definition of markdown
As per John Gruber, the original creator of Markdown:
Markdown is a text-to-HTML conversion tool for web writers. Markdown allows you to write using an easy-to-read, easy-to-write plain text format, then convert it to structurally valid XHTML (or HTML)
Here's what Markdown looks like:
Hello there! My name is **praveen yadav**.
Here's my favorite programming language list:
- Rust
- Golang
- Node.js
Why we need MDX
MDX is just an extension of Markdown that allows you to import and write JSX in your markdown documents.
Markdown can be used for creating web pages, documents or any text that needs to be transformed into other formats like For web applications, Markdown needs to be transformed into HTML. But, Using Markdown you can only use a handful of HTML elements.
MDX takes the format a step further, and allows us to include our own custom elements, in the form of React components:
import ProductList from '../components/ProductList';
Here's an list of **Awesome projects**
<ProductList
title="Awesome projects"
data={[
{ label: 'Rust', link: 'https://www.rust-lang.org/' },
{ label: 'React', value: 'https://reactjs.org/' },
{ label: 'Go', link: 'https://golang.org/' },
]}
/>
In order to display content in the browser, you need to parse the markdown file and compile it to HTML. On this blog, I am using
- gray-matter to parse my markdown files and
- mdx-bundler to compile MDX/TSX strings and give you back a component which you can render in the browser.
mdx-bundler uses esbuild, so it's VERY fast and supports TypeScript files (for the dependencies of your MDX files). It also uses @mdx-js/esbuild , an esbuild plugin to support JSX in your markdown content.
Benefits of mdx-bundler
These are popular MDX compiler that are available on the market right now -
- Official MDX support by Next.js Next.js + MDX
- Hashicorp's next-mdx-remote
- Kent C Dodds's mdx-bundler
If you have a lot of files that all import and use different components, you may benefit from using mdx-bundler, as next-mdx-remote currently only allows components to be imported and made available across all pages.
I decided to use mdx-bundler because it has a very good performance and great features like
- Components import within a mdx file
- Frontmatter supports
- Ability to use frontmatter in mdx file
- Bundle image within a mdx file using remark-mdx-images
Now you know why you pick mdx-bundler for your MDX contents, let's see how to use it with Next.JS.
bundleMDX
Next.js support two form of pre-rendering: Static Generation and Server-side Rendering. Here, we are using Static Generation to generate HTML files at build time. We will be calling bundleMDX which takes MDX source as string to compile MDX files to HTML.
const getCompiledMDX = async (source: string) => {
// Add your remark and rehype plugins here
const remarkPlugins = [];
const rehypePlugins = [];
try {
return await bundleMDX({
source: source
mdxOptions(options) {
options.remarkPlugins = [
...(options.remarkPlugins ?? []),
...remarkPlugins,
];
options.rehypePlugins = [
...(options.rehypePlugins ?? []),
...rehypePlugins,
];
return options;
},
});
} catch (error) {
throw new Error(error);
}
};
Next.JS esbuild ENOENT Issue
Sometime we get esbuild issue when it's not able to find executable path. esbuild relies on __dirname to find its executable file. In order to provide the correct path of esbuild executable, we need to provide point esbuild binary path directly in bundleMDX function.
import path from "path";
if (process.platform === "win32") {
process.env.ESBUILD_BINARY_PATH = path.join(
process.cwd(),
"node_modules",
"esbuild",
"esbuild.exe",
);
} else {
process.env.ESBUILD_BINARY_PATH = path.join(
process.cwd(),
"node_modules",
"esbuild",
"bin",
"esbuild",
);
}
getMDXComponent
MDX Bundler supplies getMDXComponent function to turn the string of compiled code into something you can mount.
import React, {useMemo} from 'react'
import {getMDXComponent} from 'mdx-bundler/client'
const Post = ({ code, frontmatter }) => {
const Component = useMemo(() => getMDXComponent(code), [code]);
return (
<div className={styles.container}>
<Component />
</div>
);
};
Conclusion
I hope this article will help you to understand how to use MDX with Next.JS using mdx-bundler. You can see a working demo at GitHub.
If you have found this useful, please consider recommending and sharing it with other fellow developers and if you have any questions or suggestions, feel free to add a comment or contact me on twitter.