Chen Yang

@next/mdx Or next-mdx-remote

I recently refactored this blog using next-mdx-remote. Previously, I used @next/mdx. I'll explain the differences between these two methods and why I chose next-mdx-remote in the article.

The Very Complex @next/mdx Way

With @next/mdx, the Next.js can read every MDX file as a page. The file structure looks like this:

- pages/
	- blog/
		- article-1.mdx
		- article-2.mdx
		- article-3.mdx
	index.tsx
	about.tsx

There is also no need to create a file like this pages/blog/[slug].jsx to dynamically display the articles. This seems very convenient. But the disadvantage is that the structure of each MDX file becomes very complex.

First, we must import the layout component that each article page requires and export a default component wrapped in this layout:

// article-1.mdx

import { PostLayout } from "@/components/Layout";

export default ({ children }) => (
<PostLayout meta={meta}>{children}</PostLayout>
);

// The main markdown content
This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block.

Second, if we require additional components in this file, we must manually import them. For example, if I created a Figure component to display images, I must import it into each post's MDX file:

// article-1.mdx

import { PostLayout } from "@/components/Layout";
import Figure from "@/component/Figure";

export default ({ children }) => (
<PostLayout meta={meta}>{children}</PostLayout>
);

This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block.

// Then use `Figure` in the main content

<Figure
  alt="slideshow screenshot"
  src="/img/blog/accessible-slideshow-with-react/slideshow-screenshot.png"
  width={900}
  height={600}
/>

Finally, we must create an object to serve as frontmatter and pass it to the default component:

// article-1.mdx

import { PostLayout } from "@/components/Layout";

export const meta = {
title: "@next/mdx Or next-mdx-remote",
description:
"When building a blog with MDX and Next.js, there are so many ways. In this article, I'll show you why I choose next-mdx-remote instead of @next/mdx.",
createdAt: "2022-06-26",
tags: ["mdx", "next.js"],
};

export default ({ children }) => (
<PostLayout meta={meta}>{children}</PostLayout>
);

// The main markdown content
This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block.

The Clean next-mdx-remote Way

With next-mdx-remote, we can write an article in normal markdown syntax:

// article-1.mdx

---

title: "@next/mdx Or next-mdx-remote"
description: "When building a blog with MDX and Next.js, there are so many ways. In this article, I'll show you why I choose next-mdx-remote instead of @next/mdx."
createdAt: "2022-06-26"
tags: ["mdx", "next.js"]

---

This is a text block.

## This Is a Heading

<Figure
  alt="slideshow screenshot"
  src="/img/blog/accessible-slideshow-with-react/slideshow-screenshot.png"
  width={900}
  height={600}
/>

This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block. This is a text block.

We place meta data between two ---, use any component without importing it in each file, and do not need to import the page layout.

To generate pages for each article, we use the Next.js way.

// pages/blog/[slug].jsx

const PostPage = ({ mdxSource, meta }) => {};

export default PostPage;

/**
 * Get artciel date in `getStaticProps` function
 */
export function getStaticProps({ params }) {
  // omitted

  return {
    props: {
      mdxSource, // The main content in each mdx file
      meta, // The frontmatter part
    },
  };
}

To use the custom components, we pass them to the MDXRemote's component prop, which is from next-mdx-remote:

// pages/blog/[slug].jsx

import { MDXRemote } from 'next-mdx-remote';
import { PostLayout } from '@/components/Layout';

const PostPage = ({ mdxSource, meta }) => {
  const components = {
    a: ({ children, ...props }) => (
      <Link {...props} passHref>
        <a>{children}</a>
      </Link>
    ),
    Figure,
  };

  return (
    <PostLayout>
      <h1>{meta.title}</h1>
      // Show the article content
      <MDXRemote {...mdxSource} components={components} />
    </PostLayout>
  );
};

export default PostPage;

// omitted

Here is the entire code.

Conclusion

Article pages can be generated from MDX files or dynamic routes; they are all static pages that are generated at build time. However, as shown in the code above, the next-mdx-remote method keeps the files cleaner and allows you to use the getStaticProps function. I hope this article could help you in deciding which to use.