Most of you have probably used Markdown files in your Gatsby.js sites, and you already know it’s an amazing way to write content. But plain Markdown is geared towards text-based content, and it can be limiting when you want to step outside of that use case. That all changes with MDX, a superset of Markdown that allows us to embed JSX directly into Markdown files. Sounds awesome, doesn’t it? In this article we’ll explore the basics of MDX with Gatsby, including some introductory techniques to help you start using it right away.
Before we dive in, you will need to have a Gatsby project that is set up and ready to edit. If you need help getting to that point, please follow the steps in Your First Steps with Gatsby v2 and then return here afterwards.
Thanks to Gatsby’s incredible plugins library, the installation process is easy! Using MDX with Gatsby only requires a single plugin, gatsby-plugin-mdx, along with the MDX peer dependencies.
Let’s install those now, like this:
$ yarn add gatsby-plugin-mdx @mdx-js/mdx @mdx-js/react
Let’s also install gatsby-source-filesystem
so that we can make use of frontmatter, generate Gatsby nodes from local files, and use import/export functionality in our MDX files:
$ yarn add gatsby-source-filesystem
While not technically required, this step is highly recommended — as it really opens the full potential of MDX content with Gatsby!
Like with all Gatsby plugins, we need to add configuration details to the plugins
section of gatsby-config.js
.
Let’s configure both gatsby-plugin-mdx
and gatsby-source-filesystem
like this:
module.exports = {
//...siteMetadata, etc
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/src/pages/`,
},
},
{
resolve: `gatsby-plugin-mdx`,
options: {
defaultLayouts: {
default: require.resolve(`./src/components/layout.js`),
},
},
// ... other plugins
],
}
Notice that we are setting a default
key in the defaultLayouts
option. This will automatically wrap all MDX files with our site’s default layout.js
component.
The gatsby-config.js file has been edited, so don’t forget to restart the development environment before proceeding!
There are several configuration options available for gatsby-plugin-mdx
:
['.mdx', '.md']
to also process normal Markdown files as MDX.default
key to auto-wrap all MDX files.)gatsby-remark-images
plugin is often used here.Full details on the usage of these options can be found in the plugin’s documentation. These docs are excellent, and I highly recommend going over them after reading this article! 🔍
The configuration we have so far can already process all .mdx
files in our site. And thanks to Gatsby’s built-in behavior, if we add them to the src/pages/
directory they will also become pages automatically!
Let’s do that now by creating a simple MDX file at src/pages/mdx-intro/index.mdx
. We’ll start off with some frontmatter and basic Markdown text, like a typical Markdown blog page would have:
---
title: MDX is Magical!
path: /mdx-intro
date: 2019-08-25
---
# Hooray For MDX!
This will be like turbo-charged Markdown!
You can view this new page by visiting http://localhost:8000/mdx-intro
in your browser.
You’ll probably recognize this page creation pattern if you went through the Your First Steps with Gatsby v2 article, the only difference being this is an MDX file instead of Markdown. This is nothing special or new so far. Let’s change that!
One of the primary features of MDX is that we can import and use JSX components right inside of Markdown content.
To demonstrate this, let’s create a simple component at /src/components/TitleBar.js
that will let us display a customized title bar.
import React from 'react';
const TitleBar = ({ text, size, bkgdColor }) => (
<div
style={{
margin: '2rem 0',
padding: '2rem',
backgroundColor: bkgdColor || '#fff',
}}
>
<h2
style={{
fontSize: size || '18px',
margin: 0,
}}
>
{text}
</h2>
</div>
);
export default TitleBar;
Next, let’s update our MDX file to look like this:
---
title: MDX is Magical!
path: /mdx-intro
date: 2019-08-25
---
import TitleBar from "../../components/TitleBar.js";
<TitleBar
size={"32px"}
bkgdColor={"#4aae9b"}
text={props.pageContext.frontmatter.title}
/>
This will be like turbo-charged Markdown!
There are two things to note here:
props.pageContext.frontmatter
. This can be quite useful, too!Important: If your MDX files contain frontmatter, always place any import statements after the frontmatter block!
Go ahead and view the updated page in your browser, and try editing the size
and bkgdColor
props to watch it update. It’s a really simple example, but again: we are using a React component inside Markdown! Pretty sweet, right?!
As mentioned in the configuration section, MDX provides us with an easy way to set up custom layouts. These layouts are convenient for wrapping additional styling and/or content around our MDX files.
We can set up default layouts for our MDX files in gatsby-config.js
, even for specific locations. Take a look at this example:
module.exports = {
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/src/pages/`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `posts`,
path: `${__dirname}/src/blog/`,
},
},
{
resolve: `gatsby-plugin-mdx`,
options: {
defaultLayouts: {
posts: require.resolve("./src/components/blog-layout.js"),
default: require.resolve("./src/components/layout.js"),
},
},
},
],
}
In this example, we have configured our site so that all MDX files sourced from the /src/blog
directory would use blog-layout.js
as a layout/wrapper. We also set up a default
config here, too.
Note: This behavior doesn’t currently seem to work as expected with MDX files sourced from the pages
directory. (But you can still wrap them with a default
layout setting, like we have currently done.)
Sometimes you will need to wrap a specific MDX file with a unique layout, or with no layout at all. This can be easily done by using JavaScript’s export default
syntax inside our MDX files, which overrides any defaultLayout
settings. We’ll cover that in the next section!
In addition to importing/using JSX components, we can also import and use other MDX files as if they were components. (Hint: they actually are!)
Let’s create a new MDX file in our components directory, at /src/components/postSignature.mdx
. We will use this at the bottom of our MDX page as an author’s signature.
##### Thanks for Reading!
*🐊 Al E. Gator | alligator.io | al@example.com*
export default ({ children }) => (
<>
{children}
</>
)
Notice the export default
statement at the bottom of the file. As mentioned in the previous section, this is how we can override our defaultLayout
configuration settings. In this case, we’re exporting an empty <>
wrapper around our signature instead.
Moving along, let’s import this MDX signature into our main MDX file, over at /src/pages/mdx-intro/index.mdx
:
---
title: MDX is Magical!
path: /mdx-intro
date: 2019-08-25
---
import TitleBar from "../../components/TitleBar.js";
import PostSignature from "../../components/postSignature.mdx";
<TitleBar
size={"32px"}
bkgdColor={"#4aae9b"}
text={props.pageContext.frontmatter.title}
/>
This is like turbo-charged Markdown!
<PostSignature />
You should now see this signature at the bottom of the mdx-intro
page. Awesome!! 😎
Thanks to the plugin combo of gatsby-plugin-mdx
and gatsby-source-filesystem
, our MDX pages are also readily available to us via GraphQL queries.
We won’t spend much time on this, as this functionality is nearly identical to querying plain Markdown files in the same manner. (The only difference is that the MDX nodes are in allMdx
and mdx
instead of allMarkdownRemark
and markdownRemark
.)
Here’s an example query that would fetch the frontmatter of all available MDX files:
query {
allMdx {
edges {
node {
frontmatter {
title
path
date(formatString: "MMMM DD, YYYY")
}
}
}
}
}
We can also provide additional data through our MDX files by using JavaScript’s export
syntax, (not to be confused with export default
as used above!) Any exported variables are added to the GraphQL schema automatically, so that we can use it when needed in GraphQL queries and/or during rendering.
Here’s some example “Food Truck Review” data that we could add to our MDX page:
export const myReviews = [
{
name: "Tim's Tacos",
overall: 9,
variety: 7,
price: 8,
taste: 9
},
{
name: "Noodleville",
overall: 7,
variety: 5,
price: 6,
taste: 8
},
{
name: "Waffle Shack",
overall: 6,
variety: 5,
price: 4,
taste: 6
},
];
After adding that anywhere in the file, we could query the data in GraphQL by accessing allMdx.nodes.exports
, like this:
query MdxExports {
allMdx {
nodes {
exports {
myReviews {
name
overall
variety
price
taste
}
}
}
}
}
This is just a really basic demo, but this functionality can be used in incredibly creative and dynamic ways.
Let’s finish up by adding a fun & practical example to our page. We’re going to use the myReviews
data that we set up above to display an animated bar chart!
First, let’s add the Recharts library to our site. This is a powerful but lightweight charting library that I use frequently in my client projects.
$ yarn add recharts
Next, we will use Recharts to create a reusable bar chart component. Since this isn’t an article about Recharts, just go ahead and create a new file at /src/components/BarChart.js
and paste in the following code:
import React, { PureComponent } from 'react';
import {
BarChart,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer,
} from 'recharts';
const colorsList = ['#008f68', '#6db65b', '#4aae9b', '#dfa612'];
class ExampleChart extends PureComponent {
render() {
return (
<div style={{ width: '100%', height: 350 }}>
<ResponsiveContainer>
<BarChart data={this.props.data}>
<CartesianGrid strokeDasharray="2 2" />
<XAxis dataKey="name" />
<YAxis type="number" domain={[0, 10]} />
<Tooltip />
<Legend />
{this.props.bars.map((bar, i) => (
<Bar
dataKey={bar}
fill={colorsList[i]}
key={`bar_${i}`}
/>
))}
</BarChart>
</ResponsiveContainer>
</div>
);
}
}
export default ExampleChart;
Now we have a nice bar chart component set up, so we just need to import and use it in the MDX page. Here’s our final version:
---
title: MDX is Magical!
path: /mdx-intro
date: 2019-08-25
---
import TitleBar from '../../components/TitleBar';
import PostSignature from '../../components/postSignature.mdx';
import BarChart from "../../components/BarChart";
export const myReviews = [
{
name: "Tim's Tacos",
overall: 9,
variety: 7,
price: 8,
taste: 9
},
{
name: "Noodleville",
overall: 7,
variety: 5,
price: 6,
taste: 8
},
{
name: "Waffle Shack",
overall: 6,
variety: 5,
price: 4,
taste: 6
},
];
<TitleBar
text={props.pageContext.frontmatter.title}
size={'32px'}
bkgdColor={'#4aae9b'}
/>
This page is built with turbo-charged Markdown!
#### My Food Reviews:
<BarChart
data={myReviews}
bars={["overall", "variety", "price", "taste"]}
/>
<PostSignature />
You should now see a sweet-looking multi-colored bar chart that animates into view, and even has animated tooltips on rollover. 📊👈
And I’ll say it again:This is all inside a Markdown (MDX) page! Just think of all the interesting blog posts and pages you can create in no time flat…
We have really covered a lot in this intro to MDX with Gatsby! Hopefully it wasn’t too overwhelming, and you can see that this combo is a total game-changer for rapid website development.
However, we only scratched the surface of what is possible. From here, I recommend digging into the Gatsby docs section on MDX. It’s a rabbit-hole worth venturing down, I promise! 🕳🐇
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!