Scott's Thoughts Pamphlet!

Gatsby File System Route API with MDX

The Gatsby File System Route API was announced recently and I have been having a play around with it. If you’re not familiar with it, it’s something like the dynamic routing you get with Next.js. If you’re not familiar with that either then it’s a way to generate your page routes from data.

This approach means that there’s no need to use the Gatsby gatsby-node.js file and related APIs to create your project pages from data.

In this walkthrough I’m going to set up the Gatsby File System Route API on a project using MDX and Chakra UI.

Why Chakra UI? I made a Getting Started Chakra UI Gatsby post recently and it’s something I want to invest a bit more time into to aid me in developing faster.

Bootstrap with a starter

With the getting started post I created a Gatsby starter I’ll base this project off of that with the Gatsby CLI:

1gatsby new \
2 gatsby-file-system-route-api-mdx \

ℹ The \ is only there so you can view them all in one column rather than having you horizontal scroll through them all.


Cool, cool, cool, now to demonstrate the System Route API I’m going to need to have some content to generate the paths with.

I’m going to jack the last three months of content from my digital garden and add it to the root of this project.

I have a particular way I like to structure my posts which is YYYY/MM/DD/folder-for-the-post/index.mdx this is nested in a content folder. (You can structure your files how you like, you do you.) I use the words folder and directory interchangeably.

So the file structure looks a bit like this:

2├─ content/2020/
3│ ├─ 09
4│ │ └─ 02/how-to-monetise-your-content/
5│ │ └─ index.mdx
6│ ├─ 10
7│ └─ 11
8├─ src/
9│ ├─ assets/
10│ ├─ components/
11│ ├─ images/
12│ ├─ pages/
13│ └─ woot-wapper.tsx
14... more files

Gatsby Source Filesystem

Cool, now there’s a bit of content time to point the gatsby-source-filesystem to it, so in this instance, as the source plugin for it is already installed I’ll copy the section pointing to the images folder and point another configuration to the content folder in the gatsby-config.js file:

2 "resolve": `gatsby-source-filesystem`,
3 "options": {
4 "name": `images`,
5 "path": `${__dirname}/content`
6 }

Gatsby MDX Plugin

Now I’m going to need something to parse the MDX files I’m using, time to install the Gatsby MDX plugin, I’ll also need the related @mdx dependencies:

1yarn add gatsby-plugin-mdx \
2 @mdx-js/mdx \
3 @mdx-js/react

And to configure that in the Gatsby plugin array in the gatsby-config.js file:

2 "resolve": `gatsby-plugin-mdx`,
3 "options": {
4 "extensions": [`.mdx`, `.md`]
5 }

Fun fact you don’t actually need to have the extensions option configured which I discovered on a stream with James Q Quick! I like to have it in there to be explicit.

Time to spin up the dev server!

Now that I have some content for the routes API to make paths for I can spin up the Gatsby development server on my machine:

1yarn dev

ℹ Tip, if you have other dev servers running on the default Gatsby port (8000) you can change it by passing flags to the Gatsby CLI, in the next example I can use the -p flag to specify the port to open on and the -o flag to open the tab on my default browser:

1yarn dev -- -p 8850 -o

Validate data with the Gatsby GraphiQL explorer

Once that’s finished I can access the Gatsby GraphiQL explorer on the localhost by adding the /___graphql to the localhost URL, in the GraphiQL I can access all the data Gatsby has available in the data layer.

Popping open the explorer I can see that I have allMdx and mdx now available to me for use in Gatsby.

So, I can start making a page off of that data, I’ll need to grab the data for one route.

First I’ll run a query so I can get a slug, why? So I can use that slug to uniquely identify that page.

I’ll query allMdx first up to list out all the MDX content to get a slug, here’s the GraphQL query:

2 allMdx {
3 nodes {
4 id
5 slug
6 frontmatter {
7 date
8 title
9 }
10 }
11 }

From that I’ll take a slug to use in a mdx query I’ll grab the slug 09/02/how-to-monitise-your-content/

2 "data": {
3 "allMdx": {
4 "nodes": [
5 {
6 "id": "4fe1c1af-d3e8-5d20-bee7-dddc6b7452f3",
7 "slug": "09/02/how-to-monetise-your-content/",
8 "frontmatter": {
9 "date": "2020-09-02T00:00:00.000Z",
10 "title": "How to Monetise Your Content With Coil and Brave BAT"
11 }
12 },
13 ... lots more data

Why am I doing that? I need the individual route data for each page to be created, to query a single page I’m using the mdx query in the GraphiQL explorer.

The mdx query is for a single route which I’m going to identify with the slug I’ve just pulled from the allMdx query.

Now I can pass in the slug via the query variables panel on the GraphiQL explorer, first up I’ll write the query that’s going to take the slug, it’ll look something like this:

1query PostBySlug($slug: String!) {
2 mdx(slug: { eq: $slug }) {
3 id
4 slug
5 # body
6 frontmatter {
7 date
8 title
9 }
10 }

In the query variables panel I can now define the value of the slug variable that the PostBySlug query (PostBySlug($slug: String!)) is expecting, that’s where slug is defined with the $ to indicate it’s a variable and the :String! is the type of the variable, in this case a string, the ! indicates that it’s a required parameter or the query wont run:

2 "slug": "09/02/how-to-monetise-your-content/"

You may notice that the body field has a # next to it, that’s commenting it out, as I’m only looking to see a result and the body content can be a bit noisy in the GraphiQL explorer, I’m leaving it in there as I’ll be using it in a page query very soon.

Pages file notation

Now that I know that the data for the MDX content I’ve added is available via the GraphiQL explorer I need to create

To use the File System Route API, I’ll use some curly bois {} in my filename to signify dynamic URL parts that relate to a field within a node.

Clear as mud?

Remember the query I made to select a single MDX page??

1query PostBySlug($slug: String!) {
2 mdx(slug: { eq: $slug }) {
3 slug
4 }

I want to reference the single MDX node with the mdx query and using the slug to identify which node.

In the magic pages directory in my gatsby project I’ll create a file detailing that I want to use the mdx query and the slug to signify the URL and wrap the file name in some curly bois:


In the file structure like this:

2... content
3├─ src/
4... other folders n' shiz
5│ ├─ pages/
6│ │ └─ {mdx.slug}.js
7... more files

I already have my Gatsby dev server running and for the file paths to be created I’ll need to stop start the dev server (Ctrl+c) then start it again yarn dev.

Validate path creation

Super duper! Now it’s time to check that the Gatsby File System Route API is doing it’s magic.

If you didn’t know already you can check the all the pages generatred by Gatsby from the Gatsby 404 page, to access it I can enter a route that doesn’t exist to see it or add the 404.js path to the localhost URL, like http://localhost:8000/404.js from here I can see that the routes have been created.

Clicking one of those routes wont do anything yet as there’s nothing in the {mdx.slug}.js file to tell Gatsby what to do!

Creating the pages

Now, to tell Gatsby what to do when one of those routes are hit, currently there’s only been a file path created.

In the {mdx.slug}.js file, I’ll first scaffold out a basic React component:

1import React from 'react'
3export default function PostPage() {
4 return (
5 <>
6 <h1>Yo!</h1>
7 </>
8 )

Clicking on any of the routes from the 404.js page will now create a page with a h1 of Yo! on there.

Now time to add a bit more data to the page, I’ll do that by using a GraphQL query in there:

1import { graphql } from 'gatsby'
2import React from 'react'
4export default function PostPage({ data }) {
5 console.log('=====================')
6 console.log(data)
7 console.log('=====================')
8 return (
9 <>
10 <h1>Yo!</h1>
11 </>
12 )
15export const query = graphql`
16 query PostBySlug($slug: String) {
17 mdx(slug: { eq: $slug }) {
18 id
19 slug
20 body
21 frontmatter {
22 date
23 title
24 }
25 }
26 }

Now that I have defined the query I want to use for the data in the page this will be made available via a data prop that is being destructured from the props.

Destructuring is a way to pull the data without having to chain it from the props, it’s a shorter way of doing this:

1export default function PostPage(props) {
2 console.log('=====================')
3 console.log(
4 console.log('=====================')
5 return (
6 ... rest of the component

Console log to check the data in the browser console gives me the results from the mdx query.

Cool, cool, cool, now I can use the MDXRenderer to render out the MDX as if it were Markdown, I’ll import that along with a Chakra UI Text component:

1import { Text } from '@chakra-ui/react'
2import { graphql } from 'gatsby'
3import { MDXRenderer } from 'gatsby-plugin-mdx'
4import React from 'react'
6export default function PostPage({ data }) {
7 const {
8 body,
9 frontmatter: { title },
10 } = data.mdx
11 return (
12 <>
13 <Text fontSize="4xl">{title}</Text>
14 <MDXRenderer>{body}</MDXRenderer>
15 </>
16 )
19export const query = graphql`
20 query PostBySlug($slug: String) {
21 mdx(slug: { eq: $slug }) {
22 id
23 slug
24 body
25 frontmatter {
26 date
27 title
28 }
29 }
30 }

Bonus content

So the page looks nice n’ all that but what about the images that are with the Markdown and opening links in there?

Ok, there’s a usual list of Gatsby remark plugins I use, these are:

  • gatsby-remark-autolink-headers > gives each heading an ID
  • gatsby-remark-copy-linked-files > opens each image in a new tab
  • gatsby-remark-smartypants > makes your punctuation look nice
  • gatsby-remark-images > displays images in the Markdown
  • gatsby-remark-external-links > links go out in a new tab when clicked

I’ll add them all and configure the gatsby-config.js file, I’ll install them via the terminal first of all:

1yarn add gatsby-remark-autolink-headers \
2 gatsby-remark-copy-linked-files \
3 gatsby-remark-smartypants \
4 gatsby-remark-images \
5 gatsby-remark-external-links

ℹ The \ is only there so you can view them all in one column rather than having you horizontal scroll through them all.

Now these can all go into the gatsbyRemarkPlugins array for the MDX plugin.

1gatsbyRemarkPlugins: [
2 `gatsby-remark-autolink-headers`,
3 `gatsby-remark-copy-linked-files`,
4 `gatsby-remark-smartypants`,
5 {
6 resolve: `gatsby-remark-images`,
7 options: {
8 maxWidth: 1200,
9 },
10 },
11 {
12 resolve: `gatsby-remark-external-links`,
13 options: {
14 target: `_blank`,
15 rel: `noopener`,
16 },
17 },

And I’ll add all of that to the gatsby-plugin-mdx config object:

2 resolve: `gatsby-plugin-mdx`,
3 options: {
4 extensions: [`.mdx`, `.md`],
5 gatsbyRemarkPlugins: [
6 `gatsby-remark-autolink-headers`,
7 `gatsby-remark-copy-linked-files`,
8 `gatsby-remark-smartypants`,
9 {
10 resolve: `gatsby-remark-images`,
11 options: {
12 maxWidth: 1200,
13 },
14 },
15 {
16 resolve: `gatsby-remark-external-links`,
17 options: {
18 target: `_blank`,
19 rel: `noopener`,
20 },
21 },
22 ],
23 },

Now my MDX is a whole lot prettier! 🎉

Recap and wrap!

Ok, that’s it for the file routes! To recap what I did:

  • Created a project from a starter with the Gatsby CLI
  • Added some content
  • Configured the Gatsby Source Filesystem
  • Added and configured the Gatsby MDX Plugin
  • Validated the content was available via the GraphiQL explorer
  • Created the dynamic page with the curly boi notation {mdx.slug}.js
  • Validated the pages were created Gatsby 404.js page
  • Used the MDXRenderer to render out the MDX on the page

The source code for this walkthough can be found on GitHub in the Gatsby File System Route Starter I made.

Back to Top

Scott Spence

Built with Gatsby · Hosted on Vercel · 2021