DEV Community 👩‍💻👨‍💻


Posted on • Updated on

How to make URL path prefix for section of website in Gatsby

Let's say we want all our Gatsby blog posts to have a /blog/ path prefix. Like:

It can be any other section but let's go with blog for this article.

Most tutorials say to put the blog posts in src/pages/blog. The post URLs will automatically come out the way we want. However, that assumes we write each blog post as a JSX component and with its own graphql query if there's markdown involved. What if we use a page template for the blog posts? It won't work anymore. Didn't for me. (Update 24 Feb 2020: it does work, I was being stupid somehow back then, I think. But this article presents another valid way to do it anyway.)

Instead of doing that, let's put blogpostTemplate.js in src/templates/blogpostTemplate and each blog post in src/blogposts/individual_post_dir/ Like this:

└── blogpostTemplate.js

└── 2020-01-01-individual-post-dir
    └── shoe-800x600.jpg
Enter fullscreen mode Exit fullscreen mode

I won't discuss how to make a page template. It's easy to find resources on that. Anyway, presumably you're here because you've already made a template and found path prefixes broken. I'll focus only on creating the path prefix.

Usual page template setup in gatsby-node.js

Now, open up gatsby-node.js in the project root directory. If you've already created a page template for the blog (or whatever other section of your site), the following code will be familiar to you.

There are two or three ways to do it. Some use async/await, some use then. I feel it's more a matter of aesthetic taste than anything else. I prefer async/await but for goodness only knows what reason I went with then for this project:

const path = require("path")
const { createFilePath } = require(`gatsby-source-filesystem`)

exports.createPages = ({ actions, graphql }) => {
  const { createPage } = actions

  return graphql(`

      blogposts: allMarkdownRemark(
        filter: { fileAbsolutePath: { glob: "**/src/blogposts/*/*.md" } }
      ) {
        edges {
          node {
            frontmatter {
            fields {   // Take note of this part
  `).then(res => {
    if (res.errors) {
      return Promise.reject(res.errors)
    }{ node }) => {
          path: node.fields.slug,  // Look, it's here again
          component: path.resolve("src/templates/blogpostTemplate.js"),
          context: {
            slug: node.fields.slug,  // We'll create it in a bit


Enter fullscreen mode Exit fullscreen mode

The main portions in the code above are:

  • const { createPage } = actions: destructure createPage from actions
  • return an allMarkdownRemark graphql query named blogposts
  • use createPage and the results in to create pages for each individual blog post using blogpostTemplate.

All pretty standard as a hundred and one tutorials out there will tell you. fields { slug } is important. It doesn't make sense now. But we'll make it in the next section. Gatsby will use this to create the path prefix.

The path prefix

Next, the exciting part. We create the path prefix. Right below the above code in gatsby-node.js, we paste in this:

exports.onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions
  if (
    node.internal.type === `MarkdownRemark` &&
  ) {
    const slug = createFilePath({ node, getNode, basePath: `src/blogposts` })
      name: `slug`,
      value: `/blog${slug}`, // Here we are, the path prefix
Enter fullscreen mode Exit fullscreen mode

Whenever we run Gatsby develop or build, Gatsby creates nodes for all our directories and markdown files. It then uses these nodes for routing and linking and whatever.

As the name of the function above suggests, onCreateNode does something upon the creation of a node. We can do whatever we want with it. For our purposes, we have an if statement that checks if the file is markdown, and if it's contained in the blogposts directory.

If it is, then we use createFilePath to create a slug. The slug is just the blog post directory concatenated with the blog post filename.

To add the /blog/ path prefix, add it to value in createNodeField as shown above: /blog${slug}.

Gatsby will now use this value to create the paths to all our blogposts.

Following the example in the directory tree above, our post will be at

Adapting the blog post template

Remember to edit blogpostTemplate.js. Tutorials usually tell us to have this query or very similar in it:

export const query = graphql`
  query($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      frontmatter {
Enter fullscreen mode Exit fullscreen mode

It looks for a path tag in the markdown frontmatter. However, we don't have path anymore. We have slug.

If you're already using slug, lucky you. If you aren't, change it:

export const query = graphql`
  query($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
Enter fullscreen mode Exit fullscreen mode

The end

For some reason, it's been frightfully difficult to find resources on how to do this. The best resource so far is actually the official Gatsby tutorial part 7. I think it's worthwhile to at least skim it for a better sense of things. In fact, I'm sure if people went through it, they'd figure things out faster than I did. But, I think, because the tutorial doesn't explicitly talk about prefixes, most people skip it.

A whole lot of people are asking about this online and trying all kinds of funny code acrobatics so I thought to write this down.

I may have misconceptions about how things are done in Gatsby. Or I've got things wrong. Or maybe you have a quicker, or more efficient way of doing this. Feel free to tell me these things.

Top comments (1)

vsevolod profile image

Useful tutorial! thanks!

🌚 Friends don't let friends browse without dark mode.

Sorry, it's true.