Code for Heaven

Go to the home page

MDX in Gatsby

In order to utilize functionality of Javascript code and React Components in your Markdown content (for example to present a button!), first you need to install appropriate plugin, so let's start in command line:

npm install @mdx-js/mdx @mdx-js/react gatsby-plugin-mdx
npm remove gatsby-transformer-remark

Code above will install mdx plugins and remove gatsby-transform-remark plugin (the one that used to transform markdown into html), that is no longer needed. Then...

Edit gatsby-config.js

In gatsby-config.js you need to replace gatsby-transformer-remark with gatsby-plugin-mdx. With one little details, this is almost it. To make it work, update plugins options to gatsbyRemarkPlugins. Here is my example:

      resolve: `gatsby-plugin-mdx`,
      options: {
        extensions: [`.md`, `.mdx`],
        gatsbyRemarkPlugins: [
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 756,

As you can see, I added one more option: extensions this tells the MDX plugin to process files with both .md and .mdx extensions as by default it only works on the latter. So if you don't want to go after every blog post you've already created and change their extension, add same option, unless you want to start using MDX features in older posts. If you're ready here, then...

Edit gatsby-node.js

First you need to change how onCreateNode filters markdown files. If you are left with old posts saved with .md extension, then you have to include both in the conditional. Otherwise go just with "Mdx" side of the alternative:

exports.onCreateNode = ({ node, actions, getNode }) => {
  const nodeInternalType = node.internal.type

-  if (node.internal.type === `MarkdownRemark`) {
+  if (nodeInternalType === `MarkdownRemark`
+      || nodeInternalType === "Mdx") {

Remember also to update graphql:

exports.createPages = async ({ actions, graphql, reporter }) => {
  const results = await graphql(`
-     allMarkdownRemark
+     allMdx

Check other files, which use graphql

Since you're not using gatsby-transform-remark plugin any more, you need to check all other files (like blog.js) and update their graphql queries so wherever they use allMarkdownRemark use allMdx.

In files that only render one post (like blogpostTemplate.js) in graphql query change markdownRemark to mdx and property name html to body.

export const pageQuery = graphql`
  query BlogPostBySlug($slug: String!) {
-    markdownRemark(frontmatter: { path: { eq: $path } }) {
-      html
+    mdx(frontmatter: { slug: { eq: $slug } }) {
+      body
      frontmatter {

And finally, inside template render function add MDXRenderer component from gatsby-plugin-mdx plugin and change destructuring assignments inside the function.

Rendering mdx requires different syntax than the one for classic markdown, so thankfully you can replace dangerouslySetInnerHTML with mdx.body wrapped with the new MDXRenderer component.

+import { MDXRenderer } from "gatsby-plugin-mdx"

export default function Template({ data }) {
-  const { markdownRemark } = data
-  const { frontmatter, html } = markdownRemark
+  const { mdx } = data
+  const { frontmatter } = mdx

-          <div
-            className="blog-post-content"
-            dangerouslySetInnerHTML={{ __html: html }}
-          />
+          <MDXRenderer>{mdx.body}</MDXRenderer>

Final notes

Remember that .mdx format follows JSX syntax, so some HTML attributes names will be required to spell differently (like class -> className).

Enjoyed it? Great! Let's stay in touch.