Banner image for "How to add markdown to a Next.js project" blog article
23 May 2025
NEW
How to add markdown to a Next.js project
Add blogs, and other documentation, to your site with ease after your follow our simple guide to include markdown in your Next.js projects.
Markdown
Next.js
React
TypeScript

Markdown

Markdown is a standardised way of writing documents to include formatting like headers, italics, bold, list, links, etc.

Normally, there is not a mechanism within frameworks to directly translate markdown into rendered HTML. This guide may help you add this functionality to a Next.js project should you wish, like us, to add blogs to your site.

Markdown itself is a lightweight markup language. You won't normally see the untranslated raw code, unless you are perhaps a Content Creator. You will normally see the rendered HTML result in a browser.

An example of markdown might look like this:

Code: Markdown
## This is a header **This is bold** _And this is italic_ [I am a link](https://futools.online) - [ ] Task List Item 1 - [ ] Task List Item 2 - [ ] Task List Item 3

Which will render to this:

-----start example-----

This is a header

This is bold

And this is italic

I am a link

  • Task List Item 1
  • Task List Item 2
  • Task List Item 3

-----end example-----

We have created an example page.mdx file, written using just markdown. It demonstrates a variety of markdown syntax.

To see it, just click Show me all the markdown.

If you are familiar with markdown you may have noticed we have tweaked our rendering of markdown, so it may look slightly different from what you are use to, but the basics are still the same as other markdown renderings.

For more information and a thorough guide on markdown, we recommend visiting the Markdown Guide page.

What you will need to know

This article assumes the reader is a developer that knows their way around Markdown, TypeScript, React.js, and [Next.js] https://nextjs.org/). Familiarity with Tailwind-css would also be useful.

For this guide it is advised to use Next.js version 15 upwards and React.js version 18 upwards. We can not ensure that your installation will work properly if you don't use this configuration.

We have also assumed that you are using the App Router configuration for Next.

Installation

You will need to install the @next/mdx package, and related packages, with this install command:

npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx

Also, it may help to read the Next.js guide: How to use markdown and MDX in Next.js to understand more.

We chose to install the remark-gfm plugin. So that we could support the GitHub Flavored Markdown Spec. This allows for autolink literals, footnotes, strikethrough, tables, task lists.

This is optional. To add remark-gfm use the install command:

npm install remark-gfm

Next.js

You will need to add the mdx imports to your next.config.ts file. It should look something like this:

Code: TypeScriptFilename: next.config.ts
import remarkGfm from "remark-gfm"; import createMDX from "@next/mdx"; import type { NextConfig } from "next"; const nextConfig: NextConfig = { // Configure `pageExtensions` to include markdown and MDX files pageExtensions: ["md", "mdx", "ts", "tsx"], }; const withMDX = createMDX({ // Add markdown plugins here, as desired extension: /\.(md|mdx)$/, options: { remarkPlugins: [remarkGfm], rehypePlugins: [], }, }); // Merge MDX config with Next.js config export default withMDX(nextConfig);

If you do not wish to use the 'remark-gfm' dependencies, just remove the line: import remarkGfm from "remark-gfm"; and the options property block.

MDX Component

You will need to create a mdx-component.tsx file. This will need to be in the root folder, i.e. the same level as your /app folder. In the case of futools, we have the /app folder as a subfolder of the /src folder, which is becoming the standard.

For a better understanding, it may be helpful to read the How to use markdown and MDX in Next.js - Add an mdx-components.tsx file section.

A mdx-component.tsx file looks something like this at the bare minimum:

Code: TSXFilename: mdx-component.tsx
import type { MDXComponents } from "mdx/types"; export function useMDXComponents(components: MDXComponents): MDXComponents { return { ...components, }; }

However, you may want to spruce it up so your final translated markdown doesn't look so bland.

The mdx-components.tsx file used at futools looks something like this (we have removed some code to avoid confusion):

Code: TSXFilename: mdx-components.tsx
import type { MDXComponents } from "mdx/types"; import { ComponentPropsWithoutRef } from "react"; import Link from "next/link"; export function useMDXComponents(components: MDXComponents): MDXComponents { return { h1: ({ children, ...props }: ComponentPropsWithoutRef<"h1">) => ( <h1 className="mb-2 mt-5" {...props}> {children} </h1> ), h2: ({ children, ...props }: ComponentPropsWithoutRef<"h2">) => ( <h2 className="mb-2 mt-5" {...props}> {children} </h2> ), h3: ({ children, ...props }: ComponentPropsWithoutRef<"h3">) => ( <h3 className="mb-2 mt-5" {...props}> {children} </h3> ), h4: ({ children, ...props }: ComponentPropsWithoutRef<"h4">) => ( <h4 className="mb-2 mt-5" {...props}> {children} </h4> ), h5: ({ children, ...props }: ComponentPropsWithoutRef<"h5">) => ( <h5 className="mb-2 mt-5" {...props}> {children} </h5> ), h6: ({ children, ...props }: ComponentPropsWithoutRef<"h6">) => ( <h6 className="mb-2 mt-5" {...props}> {children} </h6> ), p: (props: ComponentPropsWithoutRef<"p">) => ( <p className="mb-2 leading-snug" {...props} /> ), ol: (props: ComponentPropsWithoutRef<"ol">) => ( <ol className="space-y-1" style={{ listStyleType: "revert", listStyle: "revert", padding: "revert", }} {...props} /> ), ul: (props: ComponentPropsWithoutRef<"ul">) => { if (props.className === "contains-task-list") { return <ul className="space-y-1" {...props} />; } return ( <ul className="space-y-1" style={{ listStyleType: "revert", listStyle: "revert", padding: "revert", }} {...props} /> ); }, a: ({ href, children, ...props }: ComponentPropsWithoutRef<"a">) => { const className = " inline-link"; if (href?.startsWith("/")) { return ( <Link href={href} className={className} {...props}> {children} </Link> ); } if (href?.startsWith("#")) { return ( <a href={href} className={className} {...props}> {children} </a> ); } return ( <a href={href} target="_blank" rel="noopener noreferrer" className={className} {...props} > {children} </a> ); }, table: ({ children, ...props }: ComponentPropsWithoutRef<"table">) => ( <span> <table {...props}>{children}</table> </span> ), tr: ({ children, ...props }: ComponentPropsWithoutRef<"tr">) => ( <tr {...props} className="odd:bg-white even:bg-gray-100 odd:dark:bg-gray-900 even:dark:bg-gray-800" > {children} </tr> ), th: ({ children, ...props }: ComponentPropsWithoutRef<"th">) => ( <th {...props} className="border border-gray-300 p-2 dark:border-gray-700" > {children} </th> ), td: ({ children, ...props }: ComponentPropsWithoutRef<"td">) => ( <td {...props} className="border border-gray-300 p-2 dark:border-gray-700" > {children} </td> ), blockquote: (props: ComponentPropsWithoutRef<"blockquote">) => ( <blockquote className={ "ml-[0.075em] border-l-4 " + "border-gray-300 pl-4 text-gray-700 dark:border-zinc-600 dark:text-zinc-300" } {...props} /> ), pre: ({ children, ...props }: ComponentPropsWithoutRef<"pre">) => ( <pre {...props} className="mb-2"> {children} </pre> ), ...components, }; }

As you can see, for each tag you can override the default and render the HTML as you desire.

Tailwind

If you are using tailwind-css, you may need to add the new mdx-component.tsx file path to your tailwind.config.ts file so tailwind can compile it accordingly.

The tailwind.config.ts file used at futools looks something like this (again, code has been removed to avoid confusion):

Code: TypeScriptFilename: tailwind.config.ts
import type { Config } from "tailwindcss"; const config: Config = { content: [ "./src/mdx-components.tsx", ], }; export default config;

My First Markdown Page

Now you should be good to go. Now you can add a page.md or page.mdx file somewhere under your /app folder.

For example, you could make a 'mdx-test' folder: /app/mdx-test and add a file called: page.md with this content:

Code: MarkdownFilename: page.md
## This is a header **This is bold** _And this is italic_ [I am a link](https://futools.online) - [ ] Task List Item 1 - [ ] Task List Item 2 - [ ] Task List Item 3

Now goto your site to something like `http://locolhost/mdx-test' to see your rendered markdown page (we don't know your setup so this URL is just a guess 🙂).

Conclusion

The Next.js team and many others have done a lot to make adding markdown much easier, but as you can see there is still a bit of technical shenanigans needed.

We hope you enjoyed this article. We will try to keep you informed and entertained.

Please come back again and get blogging!