Markdown, Please

Published Feb 21st, 2025

I knew from the beginning making this website that I'd want the ability to write my articles using markdown, but in an effort to get something up and running quickly, I just did my first article in plain old HTML.

I thought I might need to write a preprocessor myself to convert markdown files to the correct format, but it turns Next.js already has a good solution for this!

This article from the Next.js team does a great job at explaining how to setup your Next.js application to work with markdown:

Preview image for https://nextjs.org/docs/pages/building-your-application/configuring/mdx

Configuring: MDX | Next.js

Learn how to configure MDX to write JSX in your markdown files.

Implementation for scotthaley.dev

First thing was to add the withMDX plugin to Next.js.

/next.config.ts

1import type { NextConfig } from "next";
2import createMDX from "@next/mdx";
3
4const nextConfig: NextConfig = {
5  /* config options here */
6  pageExtensions: ["js", "jsx", "md", "mdx", "ts", "tsx"],
7};
8
9const withMDX = createMDX({});
10
11export default withMDX(nextConfig);

In our next.config.ts file, we create an instance of the MDX plugin and wrap our nextConfig with withMDX().

The next thing we need to do is create a mdx-components.tsx file in our project root directory. The MDX plugin will not work without this file.

/mdx-components.tsx

1import type { MDXComponents } from "mdx/types";
2import Image, { ImageProps } from "next/image";
3
4export function useMDXComponents(components: MDXComponents): MDXComponents {
5  return {
6    h1: ({ children }) => (
7      <div className="mb-10 border-b-theme-2 border-b-2 w-full px-6">
8        <h1>{children}</h1>
9      </div>
10    ),
11    h2: ({ children }) => <h2 className="px-6">{children}</h2>,
12    h3: ({ children }) => <h3 className="px-6">{children}</h3>,
13    h4: ({ children }) => <h4 className="px-6">{children}</h4>,
14    h5: ({ children }) => <h5 className="px-6">{children}</h5>,
15    h6: ({ children }) => <h6 className="px-6">{children}</h6>,
16    p: ({ children }) => <p className="mb-4 px-6">{children}</p>,
17    ol: ({ children }) => (
18      <ol className="list-decimal mt-4 list-inside my-8 px-6">{children}</ol>
19    ),
20    img: (props) => (
21      <Image
22        sizes="100vw"
23        width={0}
24        height={0}
25        className="shadow-lg rounded-lg"
26        style={{ width: "100%", height: "auto" }}
27        {...(props as ImageProps)}
28      />
29    ),
30    ...components,
31  };
32}

There's probably a better way I could have worked out to add the padding I want (this padding is so that the horizontal line on the header is slightly larger than the width of the content) but this was a pretty quick way to solve the problem.

With the MDX plugin, your markdown will be converted to HTML tags, and this useMDXComponents function allows you to map those HTML tags to whatever you want. I've added some basic styling to my header tags and ordered lists, and I've also mapped the img tag to the Next.js Image component.

The next thing I wanted to add was a nice way to format text for the "Published" text at the top of each article.

Talking about this format...

I found I can add any arbitrary tag to this list and then use that tag in my markdown files. So I added a Accent tag so that I can format this the way I want.

/mdx-components.tsx

1import type { MDXComponents } from "mdx/types";
2import Image, { ImageProps } from "next/image";
3
4export function useMDXComponents(components: MDXComponents): MDXComponents {
5  return {
6    ...,
7    Accent: ({ children }) => (
8      <p className="mb-8 text-theme-6 text-md px-6">
9        <i>{children}</i>
10      </p>
11    ),
12    ...components,
13  };
14}

Now I can use this tag in my markdown files like so:

1# Markdown, Please
2
3<Accent>Published Feb 21st, 2025</Accent>
4
5I knew from the beginning making this website that I'd want the ability...

I also added a tag for my Note sections:

/mdx-components.tsx

1import type { MDXComponents } from "mdx/types";
2import Image, { ImageProps } from "next/image";
3
4export function useMDXComponents(components: MDXComponents): MDXComponents {
5  return {
6    ...,
7    Accent: ({ children }) => (
8      <p className="mb-8 text-theme-6 text-md px-6">
9        <i>{children}</i>
10      </p>
11    ),
12    Note: ({ children }) => (
13      <div className="px-6">
14        <div className="my-8 p-4 bg-theme-6 text-theme-1 font-semibold flex rounded-lg">
15          <div className="mr-4 font-bold">Note:</div>
16          <div>{children}</div>
17        </div>
18      </div>
19    ),
20    ...components,
21  };
22}
Note:

You will get a React error if you don't make these custom tags start with a capital letter.

Then all I had to do was convert my one article I had written to markdown! Bonus value that it's now much easier to read these pages in GitHub.