Migrate From Remark To MDX In Gatsby Using These Simple Steps

MDX VS Remark

Mdx and remark are both markdown compilers i.e. they convert markdown to HTML. So, that it can be rendered on the browser. We can also write HTML in our .md file, as the final result is HTML the compiler will process it as HTML.

Coming to Remark, It gives us the advantage of extending its functionalities using plugins. Mdx is also very similar to remark and icing on the cake is it supports all remark plugins. But what makes it so popular is the ability to process JSX in .md files. Basically, it converts the markdown files into React components making it eligible for importing and rendering other components.

This MDX’s ability becomes very useful for things like data-visualization in your blog. MDX makes blogging super fun and easy. Now lets see how we can convert an existing gatsby blog to use MDX in place of Remark.

Steps To Migrate From Remark To MDX:

1) Installing MDX plugin

We first need to install the gatsby-plugin-mdx plugin with its dependencies (@mdx-js/mdx and @mdx-js/react) .

npm install gatsby-plugin-mdx @mdx-js/mdx @mdx-js/react

Also remove the gatsby-transformer-remark plugin.

npm remove gatsby-transformer-remark

2) Replacing Remark Plugin With MDX

Go to your gatsby-config.js file, Replace plugin gatsby-transformer-remark with gatsby-plugin-mdx.

--------------------
||gatsby-config.js||
--------------------
- resolve: `gatsby-transformer-remark`
+ resolve: `gatsby-plugin-mdx`
options: {

Most of the sub-plugins of Remark works perfectly with MDX. We just need to change the plugins option to gatsbyRemarkPlugins to let MDX know that these are Remark plugins.

-------------------------
||gatsby-config.js||
-------------------------
resolve: `gatsby-plugin-mdx`
options: {
- plugins: [
+ gatsbyRemarkPlugins: [

Note: You need to add gatsby-remark-images plugin as both a sub-plugin of gatsby-plugin-mdx and a string entry in the plugins array.

-------------------------
||gatsby-config.js||
-------------------------
module.exports = {
  plugins: [
    `gatsby-remark-images`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/blog`,
        name: `blog`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/assets`,
        name: `assets`,
      },
    },
    {
      resolve: `gatsby-plugin-mdx`,
      options: {
        extensions: [`.md`, `.mdx`],
        gatsbyRemarkPlugins: [
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 590,
              linkImagesToOriginal: true,
            },
          },
          {
            resolve: `gatsby-remark-copy-linked-files`,
          },
          {
            resolve: `gatsby-remark-smartypants`,
          },
          {
            resolve: `gatsby-plugin-feed`,
          },
        ],
      },
    },
    {
      resolve: `gatsby-plugin-sharp`,
    },

3) Change File Extensions

Change your markdown files extension from .md to .mdx. This will allow MDX to recognize and process them with specified configurations.

If you don't want to change the files extention might be because of large number of files in your project. In this case you can configure MDX plugin to accept both .md and .mdx files by adding the extensions option in gatsby-plugin-mdx like this.

-------------------------
||gatsby-config.js||
-------------------------
{
    resolve:  `gatsby-plugin-mdx`,
    options:  {
        extensions:  [`.md`,  `.mdx`],
    },
},

Tip: If you use Vs-code as your code editor. Use this MDX extention for syntax highlighting and bracket matching in MDX files.

4) Changes In gatsby-node.js

In the createPages API, Replace allMarkdownRemark with allMdx.

-----------------------
||gatsby-node.js||
-----------------------
exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions;

  const blogPost = path.resolve(`./src/templates/blog-post.tsx`);
  const blogList = path.resolve(`./src/templates/blog-list.tsx`);
  const tagTemplate = path.resolve(`./src/templates/tags.tsx`);

  return graphql(
    `
      {
-       allMarkdownRemark(
+       allMdx(
          sort: { fields: [frontmatter___date], order: DESC }
          limit: 1000
        ) {
          edges {

Also, In the onCreateNode API. Where it compares the node type to create slugs, there replace MarkdownRemark to Mdx.

-----------------------
||gatsby-node.js||
-----------------------
exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions;

-  if (node.internal.type === `MarkdownRemark`) {
+  if (node.internal.type === `Mdx`) {
    const value = createFilePath({ node, getNode });
    if (typeof node.frontmatter.slug !== 'undefined') {
      createNodeField({
        node,
        name: 'slug',
        value: node.frontmatter.slug,
      });
    } else {

5) Other Replacements

In your GraphQL queries, wherever you use allMarkdownRemark change it to allMdx and markdownRemark to mdx. Also in the mdx feild in query, change html to body.

------------------------------------
||src/templates/blog-post.tsx||
------------------------------------
export const pageQuery = graphql`
  query BlogPostBySlug($slug: String!, $tag: [String!]) {
    site {
      siteMetadata {
        siteUrl
      }
    }
-    markdownRemark(fields: { slug: { eq: $slug } }) {
+    mdx(fields: { slug: { eq: $slug } }) {
      id
      excerpt(pruneLength: 160)
-      html
+      body
      fields {
        slug
      }
      frontmatter {
        title
        date(formatString: "DD MMM, YYYY")
        description
        hasJs
        tags
        cover {
          publicURL
          childImageSharp {
            fluid(maxWidth: 1170, quality: 100) {
              ...GatsbyImageSharpFluid_noBase64
            }
          }
        }
      }
    }
-    allMarkdownRemark(
+    allMdx(
      limit: 3
      sort: { fields: [frontmatter___date], order: DESC }
      filter: {
        frontmatter: { tags: { in: $tag } }
        fields: { slug: { ne: $slug } }
      }
    ) {
      edges {

Note: There is a chance that you receive an error at build time by a plugin which is quering for allMarkdownRemark. I received this error from gatsby-plugin-feed plugin to resolve this i moved this inside gatsbyRemarkPlugins option in gatsby-plugin-mdx from the main plugins array.

Now, In your post-template file import MDXRenderer component from gatsby-plugin-mdx.

-----------------------------------------------------
||src/components/post-details/post-details.tsx||
-----------------------------------------------------
import _ from 'lodash';
import { MDXRenderer } from 'gatsby-plugin-mdx';
import { Link } from 'gatsby';

Finally, Replace dangerouslySetInnerHTML to a MDXRenderer component:

-----------------------------------------------------
||src/components/post-details/post-details.tsx||
-----------------------------------------------------
  <PostDescriptionWrapper className="post_des_wrapper">
-   <PostDescription
-     dangerouslySetInnerHTML={{ __html: description }}
-     className="post_des"
-   />
+   <PostDescription className="post_des">
+     <MDXRenderer>{description}</MDXRenderer>
+   </PostDescription>
    {tags == null ? null : (
      <PostTags>
        {tags.map((tag, index) => (
          <Link key={index} to={`/tags/${_.kebabCase(tag)}/`}>
            {`#${tag}`}
          </Link>
        ))}
      </PostTags>
    )}
  </PostDescriptionWrapper>

Conclusion

Now you can use custom React components or third-party UI elements in your markdown files. Remember that MDX uses JSX in place of HTML. So, make sure that HTML in you markdown file is valid JSX. for example, if you have used class attribute in HTML component, change it to className. So, that it doesn't create a conflict when processed by MDX.

21