<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christopher Posada</title>
    <description>The latest articles on DEV Community by Christopher Posada (@christopher_posada_76c4d0).</description>
    <link>https://dev.to/christopher_posada_76c4d0</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1997727%2F6706c19d-0c63-4380-adb7-2cca04293cc5.png</url>
      <title>DEV Community: Christopher Posada</title>
      <link>https://dev.to/christopher_posada_76c4d0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/christopher_posada_76c4d0"/>
    <language>en</language>
    <item>
      <title>How to add markdown to a Next.js project</title>
      <dc:creator>Christopher Posada</dc:creator>
      <pubDate>Fri, 23 May 2025 13:07:49 +0000</pubDate>
      <link>https://dev.to/christopher_posada_76c4d0/how-to-add-markdown-to-a-nextjs-project-2bmf</link>
      <guid>https://dev.to/christopher_posada_76c4d0/how-to-add-markdown-to-a-nextjs-project-2bmf</guid>
      <description>&lt;h2&gt;
  
  
  Markdown
&lt;/h2&gt;

&lt;p&gt;Markdown is a standardised way of writing documents to include formatting like headers, italics, bold, list, links, etc.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;An example of markdown might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## This is a header&lt;/span&gt;

&lt;span class="gs"&gt;**This is bold**&lt;/span&gt;

&lt;span class="ge"&gt;_And this is italic_&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;I am a link&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://futools.online&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] Task List Item 1
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Task List Item 2
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Task List Item 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which will render to this:&lt;/p&gt;

&lt;p&gt;-----start example-----&lt;/p&gt;

&lt;h2&gt;
  
  
  This is a header
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This is bold&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;And this is italic&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://futools.online" rel="noopener noreferrer"&gt;I am a link&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Task List Item 1&lt;/li&gt;
&lt;li&gt;[ ] Task List Item 2&lt;/li&gt;
&lt;li&gt;[ ] Task List Item 3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;-----end example-----&lt;/p&gt;

&lt;p&gt;We have created an example &lt;code&gt;page.mdx&lt;/code&gt; file, written using just markdown. It demonstrates a variety of markdown syntax.&lt;/p&gt;

&lt;p&gt;To see it, just click &lt;a href="https://futools.online/blog/show-me-all-the-markdown" rel="noopener noreferrer"&gt;Show me all the markdown&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;For more information and a thorough guide on markdown, we recommend visiting the &lt;a href="https://www.markdownguide.org/" rel="noopener noreferrer"&gt;Markdown Guide&lt;/a&gt; page.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you will need to know
&lt;/h2&gt;

&lt;p&gt;This article assumes the reader is a developer that knows their way around &lt;a href="https://www.markdownguide.org/" rel="noopener noreferrer"&gt;Markdown&lt;/a&gt;, &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;, &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React.js&lt;/a&gt;, and [Next.js] &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;https://nextjs.org/&lt;/a&gt;). Familiarity with &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind-css&lt;/a&gt; would also be useful.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;We have also assumed that you are using the App Router configuration for Next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;You will need to install the &lt;code&gt;@next/mdx&lt;/code&gt; package, and related packages, with this install command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, it may help to read the Next.js guide: &lt;a href="https://nextjs.org/docs/app/guides/mdx" rel="noopener noreferrer"&gt;How to use markdown and MDX in Next.js&lt;/a&gt; to understand more.&lt;/p&gt;

&lt;p&gt;We chose to install the &lt;a href="https://github.com/remarkjs/remark-gfm" rel="noopener noreferrer"&gt;remark-gfm&lt;/a&gt; plugin. So that we could support the &lt;a href="https://github.github.com/gfm/" rel="noopener noreferrer"&gt;GitHub Flavored Markdown Spec&lt;/a&gt;. This allows for autolink literals, footnotes, strikethrough, tables, task lists.&lt;/p&gt;

&lt;p&gt;This is optional. To add &lt;code&gt;remark-gfm&lt;/code&gt; use the install command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install remark-gfm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next.js
&lt;/h2&gt;

&lt;p&gt;You will need to add the mdx imports to your &lt;code&gt;next.config.ts&lt;/code&gt; file. It should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  MDX Component
&lt;/h2&gt;

&lt;p&gt;You will need to create a &lt;code&gt;mdx-component.tsx&lt;/code&gt; file. This will need to be in the root folder, i.e. the same level as your &lt;code&gt;/app&lt;/code&gt; folder. In the case of &lt;a href="https://futools.online" rel="noopener noreferrer"&gt;futools&lt;/a&gt;, we have the &lt;code&gt;/app&lt;/code&gt; folder as a subfolder of the &lt;code&gt;/src&lt;/code&gt; folder, which is becoming the standard.&lt;/p&gt;

&lt;p&gt;For a better understanding, it may be helpful to read the &lt;a href="https://nextjs.org/docs/app/guides/mdx#add-an-mdx-componentstsx-file" rel="noopener noreferrer"&gt;How to use markdown and MDX in Next.js - Add an mdx-components.tsx file&lt;/a&gt; section.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;mdx-component.tsx&lt;/code&gt; file looks something like this at the bare minimum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import type { MDXComponents } from "mdx/types";

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    ...components,
  };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, you may want to spruce it up so your final translated markdown doesn't look so bland.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;mdx-components.tsx&lt;/code&gt; file used at &lt;a href="https://futools.online" rel="noopener noreferrer"&gt;futools&lt;/a&gt; looks something like this (we have removed some code to avoid confusion):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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&amp;lt;"h1"&amp;gt;) =&amp;gt; (
      &amp;lt;h1 className="mb-2 mt-5" {...props}&amp;gt;
        {children}
      &amp;lt;/h1&amp;gt;
    ),
    h2: ({ children, ...props }: ComponentPropsWithoutRef&amp;lt;"h2"&amp;gt;) =&amp;gt; (
      &amp;lt;h2 className="mb-2 mt-5" {...props}&amp;gt;
        {children}
      &amp;lt;/h2&amp;gt;
    ),
    h3: ({ children, ...props }: ComponentPropsWithoutRef&amp;lt;"h3"&amp;gt;) =&amp;gt; (
      &amp;lt;h3 className="mb-2 mt-5" {...props}&amp;gt;
        {children}
      &amp;lt;/h3&amp;gt;
    ),
    h4: ({ children, ...props }: ComponentPropsWithoutRef&amp;lt;"h4"&amp;gt;) =&amp;gt; (
      &amp;lt;h4 className="mb-2 mt-5" {...props}&amp;gt;
        {children}
      &amp;lt;/h4&amp;gt;
    ),
    h5: ({ children, ...props }: ComponentPropsWithoutRef&amp;lt;"h5"&amp;gt;) =&amp;gt; (
      &amp;lt;h5 className="mb-2 mt-5" {...props}&amp;gt;
        {children}
      &amp;lt;/h5&amp;gt;
    ),
    h6: ({ children, ...props }: ComponentPropsWithoutRef&amp;lt;"h6"&amp;gt;) =&amp;gt; (
      &amp;lt;h6 className="mb-2 mt-5" {...props}&amp;gt;
        {children}
      &amp;lt;/h6&amp;gt;
    ),
    p: (props: ComponentPropsWithoutRef&amp;lt;"p"&amp;gt;) =&amp;gt; (
      &amp;lt;p className="mb-2 leading-snug" {...props} /&amp;gt;
    ),
    ol: (props: ComponentPropsWithoutRef&amp;lt;"ol"&amp;gt;) =&amp;gt; (
      &amp;lt;ol
        className="space-y-1"
        style={{
          listStyleType: "revert",
          listStyle: "revert",
          padding: "revert",
        }}
        {...props}
      /&amp;gt;
    ),
    ul: (props: ComponentPropsWithoutRef&amp;lt;"ul"&amp;gt;) =&amp;gt; {
      if (props.className === "contains-task-list") {
        return &amp;lt;ul className="space-y-1" {...props} /&amp;gt;;
      }

      return (
        &amp;lt;ul
          className="space-y-1"
          style={{
            listStyleType: "revert",
            listStyle: "revert",
            padding: "revert",
          }}
          {...props}
        /&amp;gt;
      );
    },
    a: ({ href, children, ...props }: ComponentPropsWithoutRef&amp;lt;"a"&amp;gt;) =&amp;gt; {
      const className = " inline-link";
      if (href?.startsWith("/")) {
        return (
          &amp;lt;Link href={href} className={className} {...props}&amp;gt;
            {children}
          &amp;lt;/Link&amp;gt;
        );
      }
      if (href?.startsWith("#")) {
        return (
          &amp;lt;a href={href} className={className} {...props}&amp;gt;
            {children}
          &amp;lt;/a&amp;gt;
        );
      }
      return (
        &amp;lt;a
          href={href}
          target="_blank"
          rel="noopener noreferrer"
          className={className}
          {...props}
        &amp;gt;
          {children}
        &amp;lt;/a&amp;gt;
      );
    },
    table: ({ children, ...props }: ComponentPropsWithoutRef&amp;lt;"table"&amp;gt;) =&amp;gt; (
      &amp;lt;span&amp;gt;
        &amp;lt;table {...props}&amp;gt;{children}&amp;lt;/table&amp;gt;
      &amp;lt;/span&amp;gt;
    ),
    tr: ({ children, ...props }: ComponentPropsWithoutRef&amp;lt;"tr"&amp;gt;) =&amp;gt; (
      &amp;lt;tr
        {...props}
        className="odd:bg-white even:bg-gray-100 odd:dark:bg-gray-900 even:dark:bg-gray-800"
      &amp;gt;
        {children}
      &amp;lt;/tr&amp;gt;
    ),
    th: ({ children, ...props }: ComponentPropsWithoutRef&amp;lt;"th"&amp;gt;) =&amp;gt; (
      &amp;lt;th
        {...props}
        className="border border-gray-300 p-2 dark:border-gray-700"
      &amp;gt;
        {children}
      &amp;lt;/th&amp;gt;
    ),
    td: ({ children, ...props }: ComponentPropsWithoutRef&amp;lt;"td"&amp;gt;) =&amp;gt; (
      &amp;lt;td
        {...props}
        className="border border-gray-300 p-2 dark:border-gray-700"
      &amp;gt;
        {children}
      &amp;lt;/td&amp;gt;
    ),
    blockquote: (props: ComponentPropsWithoutRef&amp;lt;"blockquote"&amp;gt;) =&amp;gt; (
      &amp;lt;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}
      /&amp;gt;
    ),
    pre: ({ children, ...props }: ComponentPropsWithoutRef&amp;lt;"pre"&amp;gt;) =&amp;gt; (
      &amp;lt;pre {...props} className="mb-2"&amp;gt;
        {children}
      &amp;lt;/pre&amp;gt;
    ),
    ...components,
  };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, for each tag you can override the default and render the HTML as you desire.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind
&lt;/h2&gt;

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

&lt;p&gt;The &lt;code&gt;tailwind.config.ts&lt;/code&gt; file used at &lt;a href="https://futools.online" rel="noopener noreferrer"&gt;futools&lt;/a&gt; looks something like this (again, code has been removed to avoid confusion):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import type { Config } from "tailwindcss";

const config: Config = {
  content: [
    "./src/mdx-components.tsx",
  ],
};

export default config;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  My First Markdown Page
&lt;/h2&gt;

&lt;p&gt;Now you should be good to go. Now you can add a &lt;code&gt;page.md&lt;/code&gt; or &lt;code&gt;page.mdx&lt;/code&gt; file somewhere under your &lt;code&gt;/app&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;For example, you could make a 'mdx-test' folder: &lt;code&gt;/app/mdx-test&lt;/code&gt; and add a file called: &lt;code&gt;page.md&lt;/code&gt; with this content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## 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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now goto your site to something like `&lt;a href="http://locolhost/mdx-test" rel="noopener noreferrer"&gt;http://locolhost/mdx-test&lt;/a&gt;' to see your rendered markdown page (we don't know your setup so this URL is just a guess 🙂).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;We hope you enjoyed this article. We will try to keep you informed and entertained.&lt;/p&gt;

&lt;p&gt;Please come back again and get blogging!&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>nextjs</category>
      <category>react</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
