37
How to add MDX to an Existing Gatsby Site
My previous article featured a guide for setting up a personal website using Gatsby. One of the first items on my upgrade list for this type of project is adding support for MDX, which allows the use of React components inside Markdown files.
Code snippets are really important for a developer's blog, so I like to use a custom code component to display them. I love the look and functionality of code blocks on Gatsby's official docs:
There are loads of other neat things that you can do with MDX, like Josh Comeau's custom text emphasis using animations.
If you started your Gatsby project without MDX, this guide will show you a step-by-step walk-through for adding it to your website. You can poke around with the finished code for this tutorial in this sandbox, or check out the GitHub repo.
To get started, you need to install the @mdx-js/mdx
and @mdx-js/react
packages as well as Gatsby's official gatsby-plugin-mdx
and gatsby-plugin-feed-mdx
.
npm install --save gatsby-plugin-mdx gatsby-plugin-feed-mdx @mdx-js/mdx @mdx-js/react
In gatsby-config.js, edit the configuration for the gatsby-transformer-remark
plugin by replacing it with gatsby-plugin-mdx
:
{
- resolve: `gatsby-transformer-remark`,
+ resolve: `gatsby-plugin-mdx`,
options: {
+ extensions: [`.mdx`, `.md`],
- plugins: [
gatsbyRemarkPlugins: [ //added
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: 630,
},
},
{
resolve: `gatsby-remark-responsive-iframe`,
options: {
wrapperStyle: `margin-bottom: 1.0725rem`,
},
},
`gatsby-remark-prismjs`,
`gatsby-remark-copy-linked-files`,
`gatsby-remark-smartypants`,
],
},
},
It should now look like this:
{
resolve: `gatsby-plugin-mdx`,
options: {
extensions: [`.mdx`, `.md`],
gatsbyRemarkPlugins: [
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: 630,
},
},
{
resolve: `gatsby-remark-responsive-iframe`,
options: {
wrapperStyle: `margin-bottom: 1.0725rem`,
},
},
`gatsby-remark-prismjs`,
`gatsby-remark-copy-linked-files`,
`gatsby-remark-smartypants`,
],
},
},
In the same gatsby-config.js file, replace gatsby-plugin-feed
with gatsby-plugin-feed-mdx
.
- resolve: `gatsby-plugin-feed`,
+ resolve: `gatsby-plugin-feed-mdx`,
Then, change the plugin's configuration to replace all occurrences of allMarkdownRemark
with allMDX
and replace html
with body
in the GraphQL query:
resolve: `gatsby-plugin-feed-mdx`,
options: {
query: `
{
site {
siteMetadata {
title
description
siteUrl
site_url: siteUrl
}
}
}
`,
feeds: [
{
- serialize: ({ query: { site, allMarkdownRemark } }) => {
+ serialize: ({ query: { site, allMdx } }) => {
- return allMarkdownRemark.nodes.map(node => {
+ return allMdx.nodes.map(node => {
return Object.assign({}, node.frontmatter, {
description: node.excerpt,
date: node.frontmatter.date,
url: site.siteMetadata.siteUrl + node.fields.slug,
guid: site.siteMetadata.siteUrl + node.fields.slug,
custom_elements: [{ "content:encoded": node.html }],
})
})
},
query: `
{
- allMarkdownRemark(
+ allMdx(
sort: { order: DESC, fields: [frontmatter___date] },
) {
nodes {
excerpt
- html
+ body
fields {
slug
}
frontmatter {
title
date
}
}
}
}
`,
output: "/rss.xml",
title: "Jane Doe RSS Feed",
},
],
},
},
Now that gatsby-transformer-remark
and gatsby-plugin-feed
are no longer used, you can uninstall them by running the following commands in the terminal:
npm uninstall --save gatsby-transformer-remark gatsby-plugin-feed
Remember to save the changes in gatsby-config.js.
In the gatsby-node.js file, start by updating the GraphQL query:
const result = await graphql(
`
{
- allMarkdownRemark(
+ allMdx(
sort: { fields: [frontmatter___date], order: ASC }
limit: 1000
) {
nodes {
id
fields {
slug
}
}
}
}
`
)
The new query becomes:
const result = await graphql(
`
{
allMdx(
sort: { fields: [frontmatter___date], order: ASC }
limit: 1000
) {
nodes {
id
fields {
slug
}
}
}
}
`
)
Now edit the following line:
- const posts = result.data.allMarkdownRemark.nodes
+ const posts = result.data.allMdx.nodes
Then, in the onCreateNode
export:
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,
})
}
}
After changes, it becomes:
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `Mdx`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}
Remember to save the changes in gatsby-node.js.
In src/pages/index.js , replace occurrences of allMarkdownRemark
with allMdx
in the BlogIndex
functional component.
- const posts = data.allMarkdownRemark.nodes
+ const posts = data.allMdx.nodes
The same needs to be done in the GraphQL query.
- allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
+ allMdx(sort: { fields: [frontmatter___date], order: DESC }) {
After the change, the query becomes:
export const pageQuery = graphql`
query {
site {
siteMetadata {
title
}
}
allMdx(sort: { fields: [frontmatter___date], order: DESC }) {
nodes {
excerpt
fields {
slug
}
frontmatter {
date(formatString: "MMMM DD, YYYY")
title
description
}
}
}
}
`
Remember to save the changes in src/pages/index.js.
In src/templates/blog-post.js, replace markdownRemark
with mdx
in the BlogPostTemplate
functional component:
- const post = data.markdownRemark
+ const post = data.mdx
Also replace occurrences of markdownRemark
with mdx
in the GraphQL query, and use body
instead of html
.
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
}
}
}
`
The final query looks like this:
export const pageQuery = graphql`
query BlogPostBySlug(
$id: String!
$previousPostId: String
$nextPostId: String
) {
site {
siteMetadata {
title
}
}
mdx(id: { eq: $id }) {
id
excerpt(pruneLength: 160)
body
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
description
}
}
previous: mdx(id: { eq: $previousPostId }) {
fields {
slug
}
frontmatter {
title
}
}
next: mdx(id: { eq: $nextPostId }) {
fields {
slug
}
frontmatter {
title
}
}
}
`
Next, add an import statement for MDXRenderer
at the top of the file:
import * as React from "react"
import { Link, graphql } from "gatsby"
+ import { MDXRenderer } from "gatsby-plugin-mdx"
Next, find the the <section/>
element with the dangerouslySetInnerHTML
attribute and replace it with the MDXRenderer
component.
- <section dangerouslySetInnerHTML={{ __html: post.html }}
- itemProp="articleBody"
- />
+ <MDXRenderer>{post.body}<MDXRenderer/>
Remember to save the changes in src/templates/blog-post.js.
With all setup now complete, it's time to test that everything is working as it should. Add a new index.mdx file in content/blog/hello-mdx. Import components right in your mdx file or write some JSX:
---
title: "Hello MDX!"
date: "2021-10-25"
description: "The first post using MDX!"
---
import {Button} from './button.js'
This post is written in MDX, allowing you to embed a component after block of code which explains its creation!
js
here's a button in React!
<button onClick={alert("Hello MDX!")}>test</button>
Wow! Such button!
<Button>test</Button>
Now, run gatsby develop
in your terminal and check out your new post. The <Button>
component should be rendered as an element:
Finally, to make sure your RSS feed is being correctly generated, use gatsby build
and gatsby serve
, then navigate to localhost:9000/rss.xml. The RSS plugin does not generate a file in development mode, so you need to use a production build instead to test the functionality.
And you're done! If anything hasn't quite gone according to plan, check out the official docs for the gatsby-plugin-mdx plugin, and the gatsby-plugin-feed-mdx plugin. If you're new to MDX, Gatsby have an awesome guide for new users.
Also, if you are following along with my series on building up a personal website, you can check the other branches in the GitHub repo for updates to the tutorial project.
And if you get stuck, you can always hit me up on Twitter for help!
This article was originally published on my website.
Cheers!
37