DEV Community

Cover image for Migrating Gatsby Remark Blog to MDX
Sasmitha Manathunga
Sasmitha Manathunga

Posted on

Migrating Gatsby Remark Blog to MDX

In this article, I'll show you how to migrate your regular Gatsby Markdown blog to use MDX.

Setting up our MDX blog

MDX lets you use JSX with Markdown. If you have an existing Gatsby blog using Markdown, here's a quick guide for migrating to MDX.

As a starting point, we'll use Gatsby starter blog to quickly set up our blog. Let's turn this regular Markdown blog to use MDX.

First, open up the terminal and create a new site with gatsby-starter-blog template.

gatsby new my-blog https://github.com/gatsbyjs/gatsby-starter-blog
Enter fullscreen mode Exit fullscreen mode

Now navigate to the site's directory.

cd my-blog
Enter fullscreen mode Exit fullscreen mode

Adding The MDX Plugin

First, we need to add MDX support for our blog. Let's install the gatsby-plugin-mdx plugin and its dependencies. Also, remove the existing gatsby-transformer-remark plugin.

npm install @mdx-js/mdx@v1 @mdx-js/react@v1 gatsby-plugin-mdx
npm remove gatsby-transformer-remark
Enter fullscreen mode Exit fullscreen mode

You should install the correct versions; otherwise, it wouldn't work.

Now we need to update the gatsby-config.js file. Replace gatsby-transformer-remark with gatsby-plugin-mdx and change the plugins option to gatsbyRemarkPlugins.

// gatsby-config.js

{
-     resolve: `gatsby-transformer-remark`,
+     resolve: `gatsby-plugin-mdx`,
      options: {
-       plugins: [
+       gatsbyRemarkPlugins: [
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 630,
            },
          },
          // Other Gatsby remark plugins
        ],
      },
}

Enter fullscreen mode Exit fullscreen mode

Updating gatsby-node.js

Next, we need to tell Gatsby to use MDX. Go to gatsby-node.js and in the GraphQL query replace allMarkdownRemark with allMdx.

// gatsby-node.js

const result = await graphql(
    `
      {
-       allMarkdownRemark
+       allMdx(
          sort: { fields: [frontmatter___date], order: ASC }
          limit: 1000
        ) {
          nodes {
            id
            fields {
              slug
            }
          }
        }
      }
    `
  )
Enter fullscreen mode Exit fullscreen mode

Also, change the posts constant.

// gatsby-node.js
- const posts = result.data.allMarkdownRemark.nodes
+ const posts = result.data.allMdx.nodes
Enter fullscreen mode Exit fullscreen mode

Additionally, we need to change onCreateNode to use Mdx instead of MarkdownRemark.

// 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 })

    createNodeField({
      name: `slug`,
      node,
      value,
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

Finally, on the createSchemaCustomization hook, change MarkdownRemark to Mdx

// gatsby-node.js
exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes } = actions

  createTypes(`
    // Previous types
-   type MarkdownRemark implements Node {
+   type Mdx implements Node {
      frontmatter: Frontmatter
      fields: Fields
    }
    // Other stuff

  `)
}
Enter fullscreen mode Exit fullscreen mode

Updating the Blog Post Template

Next, we need to update our blog post template in src/templates/blog-post.js. The MDXRenderer component renders the contents of the .mdx file, so let's import it.

// src/templates/blog-post.js

import { MDXRenderer } from 'gatsby-plugin-mdx';
Enter fullscreen mode Exit fullscreen mode

Now in the GraphQL query, replace all occurrences of markdownRemark with mdx and change the html field to body.

// src/templates/blog-post.js

export const pageQuery = graphql`
  query BlogPostBySlug(
    $id: String!
    $previousPostId: String
    $nextPostId: String
  ) {
    site {
      siteMetadata {
        title
      }
    }
-   markdownRemark(id: { eq: $id }) {
+   mdx(id: { eq: $id }) {
      id
      excerpt(pruneLength: 160)
-     html
+     body
      frontmatter {
        title
        date(formatString: "MMMM DD, YYYY")
        description
      }
    }
-   previous: markdownRemark(id: { eq: $previousPostId }) {
+   previous: mdx(id: { eq: $previousPostId }) {
      fields {
        slug
      }
      frontmatter {
        title
      }
    }
-   next: markdownRemark(id: { eq: $nextPostId }) {
+   next: mdx(id: { eq: $nextPostId }) {
      fields {
        slug
      }
      frontmatter {
        title
      }
    }
  }
`

Enter fullscreen mode Exit fullscreen mode

Remove the section tag and add the MDXRenderer component. Then change the post constant to use mdx.

// src/templates/blog-post.js
- <section dangerouslySetInnerHTML={{ __html: post.html }} itemProp="articleBody"/>
+ <MDXRenderer>{post.body}</MDXRenderer>
Enter fullscreen mode Exit fullscreen mode
// src/templates/blog-post.js
- const post = data.markdownRemark
+ const post = data.mdx
Enter fullscreen mode Exit fullscreen mode

Updating the Home Page

The src/pages/index.js file, which renders the home page needs to be changed. So similar to what we did in src/templates/blog-post.js, we need to change all occurrences of allMarkdownRemark with allMdx.

First, change the posts constant

// src/pages/index.js
- const posts = data.allMarkdownRemark.nodes
+ const posts = data.allMdx.nodes
Enter fullscreen mode Exit fullscreen mode

Then change the GraphQL query.

// src/pages/index.js
export const pageQuery = graphql`
  query {
    site {
      siteMetadata {
        title
      }
    }
-   allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
+   allMdx(sort: { fields: [frontmatter___date], order: DESC }) {
      nodes {
        excerpt
        fields {
          slug
        }
        frontmatter {
          date(formatString: "MMMM DD, YYYY")
          title
          description
        }
      }
    }
  }
`
Enter fullscreen mode Exit fullscreen mode

Check whether any pages use allMarkdownRemark and change them to allMdx.


Updating File Extensions

Since we don't have any .mdx files, let's tell Gatsby to accept both .md and .mdx extensions, so we can still use our existing .md files. In gatsby-config.js add the extensions option to gatsby-plugin-mdx and pass it an array of extensions.

// gatsby-config.js

{
    resolve: `gatsby-plugin-mdx`,
    options: {
+     extensions: [`.md`, `.mdx`],
      // Other options
    },
  },
Enter fullscreen mode Exit fullscreen mode

Now you just need to hit gatsby develop and see your wonderful
MDX blog up and running.


Fixing Errors

Ok, that probably didn't work. So here're some errors I've faced and
how I fixed them.

1. GraphQL error

You probably missed one of the above steps. Go through it again and find out what you missed. Also, check whether all GraphQL queries are valid.

2.Error: MDB_PROBLEM: Unexpected problem - txn should abort

Make sure you changed the createSchemaCustomization to use Mdx
instead of MarkdownRemark. If this didn't work, try clearing the Gatsby cache.

gatsby clean
Enter fullscreen mode Exit fullscreen mode

In my case, the issue still persisted, so I had to clear the npm cache.

npm cache clean --force
Enter fullscreen mode Exit fullscreen mode

3. Unknown: Unterminated JSX contents.

Make sure you have valid JSX. Here's a couple of things (not an exhaustive list) to look out for:

  • Change the class attribute to className.
  • Make sure all <img/> and <br/> tags are self closing.
  • Write inline styles as objects.

4. Error: EPERM: operation not permitted

Try running gatsby clean before setting up the development server with gatsby develop.


Conclusion

And that's it! We got our MDX blog up and running. Enter gatsby develop and see your brand new MDX blog. Now you can use any React component in your .mdx files. Happy Blogging!

Top comments (0)