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 \3 https://github.com/spences10/gatsby-starter-chakra-ui
ℹ The \
is only there so you can view them all in one column rather
than having you horizontal scroll through them all.
Content
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:
1gatsby-file-system-route-api-mdx/2├─ content/2020/3│ ├─ 094│ │ └─ 02/how-to-monetise-your-content/5│ │ └─ index.mdx6│ ├─ 107│ └─ 118├─ src/9│ ├─ assets/10│ ├─ components/11│ ├─ images/12│ ├─ pages/13│ └─ woot-wapper.tsx14... 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:
1{2 "resolve": `gatsby-source-filesystem`,3 "options": {4 "name": `images`,5 "path": `${__dirname}/content`6 }7},
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:
1{2 "resolve": `gatsby-plugin-mdx`,3 "options": {4 "extensions": [`.mdx`, `.md`]5 }6},
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:
1{2 allMdx {3 nodes {4 id5 slug6 frontmatter {7 date8 title9 }10 }11 }12}
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/
1{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 id4 slug5 # body6 frontmatter {7 date8 title9 }10 }11}
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:
1{2 "slug": "09/02/how-to-monetise-your-content/"3}
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 slug4 }5}
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:
1{mdx.slug}.js
In the file structure like this:
1gatsby-file-system-route-api-mdx/2... content3├─ src/4... other folders n' shiz5│ ├─ pages/6│ │ └─ {mdx.slug}.js7... 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'2
3export default function PostPage() {4 return (5 <>6 <h1>Yo!</h1>7 </>8 )9}
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'3
4export default function PostPage({ data }) {5 console.log('=====================')6 console.log(data)7 console.log('=====================')8 return (9 <>10 <h1>Yo!</h1>11 </>12 )13}14
15export const query = graphql`16 query PostBySlug($slug: String) {17 mdx(slug: { eq: $slug }) {18 id19 slug20 body21 frontmatter {22 date23 title24 }25 }26 }27`
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(props.data)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'5
6export default function PostPage({ data }) {7 const {8 body,9 frontmatter: { title },10 } = data.mdx11 return (12 <>13 <Text fontSize="4xl">{title}</Text>14 <MDXRenderer>{body}</MDXRenderer>15 </>16 )17}18
19export const query = graphql`20 query PostBySlug($slug: String) {21 mdx(slug: { eq: $slug }) {22 id23 slug24 body25 frontmatter {26 date27 title28 }29 }30 }31`
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 IDgatsby-remark-copy-linked-files
> opens each image in a new tabgatsby-remark-smartypants
> makes your punctuation look nicegatsby-remark-images
> displays images in the Markdowngatsby-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 },18],
And I’ll add all of that to the gatsby-plugin-mdx
config object:
1{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 },24}
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