Create an MDX powered Gatsby site from scratch

Gatsby is a static site generator built on React that provides a delightful developer experience. Using Gatsby with MDX allows us to create rich content using familiar, readable syntax stored right in our source code.

Initializing a new, default Gatsby site

Install the Gatsby CLI and use it to initialize a new Gatsby site with the default starter. These steps are adapted from the official Gatsby Quick Start.

npm install -g gatsby-cli
gatsby new gatsby-site
cd gatsby-site
npm start

You should see the site running at localhost:8000.

Gatsby Default Starter

Let's get familiar with the project structure by making a small change. Open the project in your code editor, and navigate to src/pages/index.js. Change the <h1> text to something else, like "Hello world". Save, and observe your changes in the browser.

7f49e8d

Adding MDX support

"MDX is an authorable format that lets you seamlessly write JSX in your Markdown documents." — mdxjs.com

To add MDX support, we will follow the steps detailed in gatsby-plugin-mdx. I've pulled them out below for convenience.

npm install --save gatsby-plugin-mdx @mdx-js/mdx @mdx-js/react

Add gatsby-plugin-mdx to the plugins array found in gatsby-config.js.

gatsby-config.js
module.exports = {
  plugins: [
+
    `gatsby-plugin-mdx`,
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },

04b5d73

You can now create MDX pages. To test this out, create a new MDX file, page-3.mdx inside the pages directory with some markdown. With the power of MDX, you could also add your own React components right next to your markdown.

src/pages/page-3.mdx
+
# Page 3
+
+
MDX is working!
+
+
```
+
Some code
+
```

Restart your server (CTRL+C then npm start) and navigate to http://localhost:8000/page-3 to observe the MDX page working.

eb52289

Create pages from content

Next we'll add a feature to create pages or routes from files outside of the pages directory. In this example, we will create pages from a new directory, content. Every file inside the content/ directory will be its own page.

First, install gatsby-plugin-page-creator. We'll use this to automatically create pages from React components in directories of our choosing.

npm install --save gatsby-plugin-page-creator

Configure the plugin to create pages from files within src/content/.

gatsby-config.js
module.exports = {
  plugins: [
+
    {
+
      resolve: `gatsby-plugin-page-creator`,
+
      options: {
+
        path: `${__dirname}/src/content`
+
      }
+
    },
    `gatsby-plugin-mdx`,
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },

Now create the src/content/ directory and add an MDX file with some frontmatter.

src/content/post.mdx
+
---
+
title: Post 1
+
---
+
+
This is a post.

Restart your server and navigate to localhost:8000/post to see it working.

This also works with nested directories. If you create the file src/content/case-studies/projects.mdx, Gatsby will create a page at localhost:8000/case-studies/project

599d27d

Display content in your components

We're now creating pages from MDX files within the content directory (and sub-directories)! 🎉

However, we don't have a way to interact with those pages from our components. Adding this feature is useful for creating things like a list of recent blog posts.

We'll use gatsby-source-filesystem to provide our application with data about our files, and a Gatsby Page Query to provide data to our component.

First, let's tell our Gatsby app about the content folder by adding this object to our plugins array:

gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: `gatsby-plugin-page-creator`,
      options: {
        path: `${__dirname}/src/content`
      }
    },
    `gatsby-plugin-mdx`,
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
+
    {
+
      resolve: `gatsby-source-filesystem`,
+
      options: {
+
        name: `content`,
+
        path: `${__dirname}/src/content`,
+
      }
+
    },

Next, create a new page, content.js. We'll use this to display a list of all of our content pages.

src/pages/content.js
+
import React from 'react'
+
+
export default (props) => {
+
  return (
+
    <>
+
      <h1>Content</h1>
+
      <ul>
+
        <li>Content placeholder</li>
+
      </ul>
+
    </>
+
  )
+
}

To supply this component with information about our MDX content, we need to create a page query. Gatsby automatically looks for a single exported graphql string in page components and passes the result of the GraphQL query to your component as a prop named data.

src/pages/content.js
1
import React from 'react'
1
import React from 'react'
2
+
import { graphql } from 'gatsby'
2
-
export default (props) => {
3
+
export default ({ data }) => {
4
+
  console.log('data', data)
5
+
  const { edges } = data.allMdx
6
+
3
  return (
7
  return (
4
    <>
8
    <>
5
      <h1>Content</h1>
9
      <h1>Content</h1>
6
      <ul>
10
      <ul>
7
-
        <li>Content placeholder</li>
11
+
        {edges.map(item => (
12
+
          <li>{item.node.frontmatter.title}</li>
13
+
        ))}
8
      </ul>
14
      </ul>
9
    </>
15
    </>
10
  )
16
  )
11
}
17
}
18
+
19
+
export const pageQuery = graphql`
20
+
  query ContentIndex {
21
+
    allMdx(
22
+
      filter: {
23
+
        fileAbsolutePath: {glob: "**/content/**"}
24
+
      }
25
+
    ) {
26
+
      edges {
27
+
        node {
28
+
          id
29
+
          frontmatter {
30
+
            title
31
+
          }
32
+
        }
33
+
      }
34
+
    }
35
+
  }
36
+
`

Restart your development server, and observe the output of the console log. We use this data to render a list of content pages.

6b03abf

During the Gatsby build process, we need to add some extra data to each MDX node, specifically the slug used for the URL. This will eventually get passed to our component and used to link to each page.

We'll hook in to the onCreateNode API and add a field called slug to each MDX node.

gatsby-node.js
+
const { createFilePath } = require('gatsby-source-filesystem')
+
+
exports.onCreateNode = (args) => {
+
  const { node, actions, getNode } = args
+
  const { createNodeField } = actions
+
+
  if (node.internal.type === 'Mdx') {
+
    const value = createFilePath({ node, getNode })
+
+
    createNodeField({
+
      name: 'slug',
+
      node,
+
      value
+
    })
+
  }
+
}

Now that each MDX node has its own slug, update the GraphQL page query with the slug field.

src/pages/content.js
export const pageQuery = graphql`
  query ContentIndex {
    allMdx(
      filter: {
        fileAbsolutePath: {glob: "**/content/**"}
      }
    ) {
      edges {
        node {
          id
          frontmatter {
            title
          }
+
          fields {
+
            slug
+
          }
        }
      }
    }
  }
`

With this new data, update the content component to include a link to each page.

src/components/content.js
1
import React from 'react'
1
import React from 'react'
2
-
import { graphql } from 'gatsby'
2
+
import { graphql, Link } from 'gatsby'
3
3
4
export default ({ data }) => {
4
export default ({ data }) => {
5
  const { edges } = data.allMdx
5
  const { edges } = data.allMdx
6
6
7
  return (
7
  return (
8
    <>
8
    <>
9
      <h1>Content</h1>
9
      <h1>Content</h1>
10
      <ul>
10
      <ul>
11
        {edges.map(item => (
11
        {edges.map(item => (
12
-
          <li>{item.node.frontmatter.title}</li>
12
+
          <li>
13
+
            <Link to={item.node.fields.slug}>
14
+
              {item.node.frontmatter.title}
15
+
            </Link>
16
+
          </li>
13
        ))}
17
        ))}
14
      </ul>
18
      </ul>
15
    </>
19
    </>
16
  )
20
  )
17
}
21
}

af6558a

Restart your server, and try it out! Here's how mine turned out.

Content page with page links

Conclusion

You now have the ground work laid for an MDX powered Gatsby site. By configuring Gatsby to create pages out of your MDX files, and then rendering that content in your components, you have a powerful starting point for creating amazing content.

Thank you Tony and Prince for proofreading and encouraging me.