MDX (Markdown + JSX) is an elegant way to manage content-heavy websites while integrating React components directly into Markdown. It’s a favorite of mine for personal and professional projects. Setting it up in Gatsby has always been refreshingly simple—what I’d call stupid-simple. In contrast, Next.js, while still straightforward, requires a few additional steps that made the process feel a bit less effortless.
In Gatsby, adding MDX to your project is almost frictionless:
npm install gatsby-plugin-mdx gatsby-source-filesystem @mdx-js/react
gatsby-config.js
to configure the plugin and file sources:
module.exports = {
plugins: [
`gatsby-plugin-mdx`,
{
resolve: `gatsby-source-filesystem`,
options: { name: `content`, path: `${__dirname}/content/` },
},
],
};
.mdx
files into the content
directory, and Gatsby’s GraphQL layer automatically indexes them, making metadata and content accessible via queries.From there, you can create an MDX renderer component and link everything up without much effort. Gatsby handles routing, frontmatter parsing, and even some styling out of the box.
When I set out to build this blog in Next.js, I discovered that while the framework supported MDX well, the setup was slightly more involved. Here’s a breakdown of the steps:
Install the MDX packages:
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx gray-matter
Update the next.config.js
:
Configure Next.js to use MDX as part of the build pipeline. My configuration looked like this:
const withMDX = require('@next/mdx')({
extension: /\.mdx?$/,
});
module.exports = withMDX({
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'mdx'],
});
Routing and Metadata: Unlike Gatsby, Next.js doesn’t have a built-in GraphQL layer for querying frontmatter metadata. I had to write custom logic using gray-matter
to parse metadata from MDX files and generate routes dynamically. Here’s a sample:
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
const postsDirectory = path.join(process.cwd(), 'content');
export function getPostData(slug) {
const filePath = path.join(postsDirectory, `${slug}.mdx`);
const fileContents = fs.readFileSync(filePath, 'utf8');
const { data, content } = matter(fileContents);
return { slug, ...data, content };
}
Writing this wasn’t overly complicated, but it added a layer of manual setup that Gatsby’s plugin system abstracts away.
Markdown Styling: Next.js doesn’t provide default styling for Markdown content, so I installed the @tailwindcss/typography
plugin and added the prose
class to my components. This step was small but something Gatsby users might not expect.
Dealing with Frontmatter Rendering: Initially, I found that the frontmatter YAML was rendering at the top of my pages, which caught me off guard. This behavior wasn’t immediately explained in the Next.js MDX documentation. After some troubleshooting and research, I discovered the solution: using next-mdx-remote
. This additional library allowed me to cleanly separate MDX source files from the app’s source code and address the frontmatter issue.
One of the most helpful resources in navigating these challenges was a blog post by Space Jelly. It explained the nuances of using next-mdx-remote
and helped bridge the gaps in the official documentation. This guide clarified not only how to prevent frontmatter from rendering but also how to use the library to manage remote MDX content efficiently.
The process highlighted key differences in the philosophies of Gatsby and Next.js:
In the end, I appreciated the control and customization Next.js offered, but I also missed the “stupid-simple” experience Gatsby provides. Despite the additional steps, the process wasn’t overwhelming, and it resulted in a more tailored solution for my needs.
2024-11-27