#migrate to @next/mdx

1 messages · Page 1 of 1 (latest)

wind quiver
#
"use cache";

import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { cwd } from "node:process";
import { compileMDX } from "next-mdx-remote/rsc";
import { type BlogFrontmatter } from "@/utils/types";
import { walkDir } from "@/utils/walk-dir";

export async function getArticles() {
  const articlesPath = resolve(cwd(), "private/contents/news");
  const filePaths = walkDir(articlesPath, ".mdx");

  const articles = await Promise.all(
    filePaths.map(async (filePath) => {
      const source = readFileSync(filePath, "utf8");
      const { frontmatter, content } = await compileMDX<BlogFrontmatter>({
        source,
        options: {
          parseFrontmatter: true,
          mdxOptions: { useDynamicImport: true },
        },
      });

      return {
        slug: frontmatter.slug,
        title: frontmatter.title,
        description: frontmatter.description,
        author: frontmatter.author,
        thumbnail: frontmatter.thumbnail,
        rawDate: new Date(frontmatter.date),
        content,
      };
    }),
  );

  const sorted = articles.sort(
    (a, b) => b.rawDate.getTime() - a.rawDate.getTime(),
  );

  return sorted.map(({ rawDate, ...rest }) => ({
    ...rest,
    date: rawDate.toLocaleDateString("en-US", {
      month: "long",
      day: "numeric",
      year: "numeric",
    }),
  }));
}

export type Articles = Awaited<ReturnType<typeof getArticles>>;
manic abyssBOT
#

Hi @wind quiver. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

To migrate your code from using next-mdx-remote's compileMDX to the official @next/mdx approach in Next.js (App Router), you need to change how you handle MDX files and frontmatter. With @next/mdx, you don't use a runtime compile function like compileMDX. Instead, you import .mdx files directly, and you can export metadata (frontmatter) from those files using ES module exports.

Here's how you can migrate:

1. Configure @next/mdx in your project

Update your next.config.mjs to use @next/mdx and include .mdx in pageExtensions:

import createMDX from '@next/mdx'

/** @type {import('next').NextConfig} */
const nextConfig = {
  pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
}

const withMDX = createMDX({
  // Add remark/rehype plugins here if needed
})

export default withMDX(nextConfig)

How to use markdown and MDX in Next.js

2. Add an mdx-components.tsx file

This is required for MDX to work with the App Router:

import type { MDXComponents } from 'mdx/types'

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    ...components,
  }
}

mdx-components.tsx file convention

3. Export frontmatter from your MDX files

Instead of parsing frontmatter at runtime, you can export metadata directly from your MDX files:

export const metadata = {
  slug: "my-article",
  title: "My Article",
  description: "This is a description.",
  author: "Author Name",
  thumbnail: "/path/to/thumbnail.jpg",
  date: "2024-06-01",
}

# My Article

Content goes here...

Frontmatter in MDX with @next/mdx

4. Import MDX files and their metadata

#

You can now import the MDX file and its metadata directly:

import Article, { metadata } from '@/private/contents/news/my-article.mdx'

export async function getArticles() {
  // You can use dynamic imports or require.context to import all MDX files
  // For each, access the metadata export
  return [
    {
      slug: metadata.slug,
      title: metadata.title,
      description: metadata.description,
      author: metadata.author,
      thumbnail: metadata.thumbnail,
      rawDate: new Date(metadata.date),
      content: <Article />,
    },
    // ...repeat for other articles
  ]
}

Referencing metadata from MDX files

5. Notes

  • You no longer need to use compileMDX or parse frontmatter at runtime.
  • If you want to programmatically import all MDX files, you may need to use dynamic imports or a custom script to gather all MDX modules and their metadata.
  • If you still want to use YAML frontmatter, you can use remark plugins like remark-frontmatter and remark-mdx-frontmatter in your MDX config, but the recommended approach is to use ES module exports for metadata.

Summary:
With @next/mdx, you import MDX files and their metadata directly, removing the need for runtime compilation and frontmatter parsing. Update your MDX files to export metadata, and import them in your code as shown above.
How to use markdown and MDX in Next.js

If you need to process a directory of MDX files, you'll need to use dynamic imports or a build-time script to collect all metadata exports. The knowledge sources do not provide a built-in API for bulk-importing MDX files, so this part may require some custom logic.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: