Notes

Dynamically generate Mdx pages based on file name

Edit on GitHub

Gatsby
3 minutes

I followed some article to setup generating pages for MDX content. That tutorial used the path value defined in frontmatter to build the page. Which is bothersome because now i gotta specify a path in frontmatter every time. Otherwsie the build will fail. I’m coming from Jekyll and Hugo, and the path was always optional.

I’d much rather it take the filename as the basis for the URL, which i have now managed to do, by following yet another article.

Here’s the code for both.

The first one is generating pages based on the path value and then i’m using this on the index pages and applying a filter to get posts from different folders

 1const path = require('path')
 2
 3exports.createPages = async ({ graphql, actions, reporter }) => {
 4  const { createPage } = actions
 5  const result = await graphql(`
 6    query {
 7      allMdx {
 8        edges {
 9          node {
10            id
11            frontmatter {
12              path
13            }
14          }
15        }
16      }
17    }
18  `)
19
20  if (result.errors) {
21    reporter.panicOnBuild('🚨  ERROR: Loading "createPages" query', result.errors)
22  }
23  // Create blog post pages.
24  const posts = result.data.allMdx.edges
25  // you'll call `createPage` for each result
26  posts.forEach(({ page }, index) => {
27    createPage({
28      path: page.frontmatter.path,
29      component: path.resolve(`./src/components/Layout/Post.jsx`),
30      context: {
31        id: page.id,
32      },
33    })
34  })
35}

And this one is generating pages based on filename and taking them from the notes directory. I don’t have to add any path values to the frontmatter

 1const { createFilePath } = require('gatsby-source-filesystem')
 2const path = require('path')
 3
 4// Add a field called 'slug', which is basically filename + directory
 5exports.onCreateNode = ({ node, getNode, actions }) => {
 6  const { createNodeField } = actions
 7
 8  if (node.internal.type === 'Mdx') {
 9    const slug = createFilePath({
10      node,
11      getNode,
12      basePath: `notes`,
13      trailingSlash: false,
14    })
15
16    createNodeField({
17      name: 'slug',
18      node,
19      value: `${slug}`,
20    })
21  }
22}
23
24// Use that slug field to create pages
25exports.createPages = async ({ graphql, actions, reporter }) => {
26  const { createPage } = actions
27
28  const result = await graphql(`
29    query {
30      allMdx {
31        edges {
32          node {
33            id
34            fields {
35              slug
36            }
37          }
38        }
39      }
40    }
41  `)
42  if (result.errors) {
43    reporter.panicOnBuild('🚨  ERROR: Loading "createPages" query', result.errors)
44  }
45
46  const posts = result.data.allMdx.edges
47  posts.forEach(({ node }, index) => {
48    createPage({
49      path: node.fields.slug,
50      component: path.resolve(`./src/components/Layout/Post.jsx`),
51      context: { id: node.id },
52    })
53  })
54}

Now i want it to use path (if defined) for custom URL slug and use filename as a default fallback. But that bit i’ll get to at some other time..