Part 6: Create Pages Programmatically

Introduction

In Part 5, you added all of your blog posts to your Blog page. But that means that your Blog page will get longer and longer as you add more posts to your site. It would be better if each post lived on its own page, and then your Blog page could link out to all the different posts.

So far, the way you’ve created new pages for your Gatsby site is by creating a new file in the src/pages directory and hard-coding the page’s contents in JSX. But manually creating a new page for each post would be quite repetitive, especially since each page has the same structure: render the frontmatter and contents of an MDX file.

In this part of the Tutorial, you’ll learn how to create new pages dynamically using Gatsby’s Filesystem Route API.

By the end of this part of the Tutorial, you will be able to:

  • Use Gatsby’s Filesystem Route API to dynamically create new pages for your blog posts.
  • Add a query variable to a page query.

Prefer a video?

If you’d rather follow along with a video, here’s a recording of a livestream that covers all the material for Part 6.

You can catch the stream live on Wednesdays at 10AM Pacific Time / 5PM UTC on the Gatsby Twitch channel.

Create new routes dynamically with Gatsby’s File System Route API

When you build your Gatsby site, Gatsby creates a new route for each page component in your src/pages directory. So far, you’ve only been building one page per file: the index.js file creates the Home page, the about.js file creates the About page, and the blog.js file creates the Blog page.

But you can also use one page component to create multiple pages. Instead of hard-coding all the contents, you’ll create a template to outline the basic structure of your page, and then Gatsby can dynamically add in the specific data for each page at build time. To do that, you’ll use Gatsby’s File System Route API, which lets you create routes dynamically by naming your page files with a special syntax.

Key Gatsby Concept: File System Route API

Gatsby’s File System Route API defines a special syntax for naming the files in your src/pages directory, which lets you dynamically create new pages for your site based on a collection of nodes in the data layer.

For example, imagine your site had a bunch of Product nodes in the data layer. You could use the File System Route API to create one product page template component. Then, when you built your site, Gatsby would combine that page template with the data for each Product node and generate a new page for each product. And if you decided you needed to make changes to your product page, you’d only have to edit the template component, and Gatsby would update all your product pages the next time it rebuilt the site.

To create a collection route:

  1. Decide what type of node you want to create pages from.
  2. Choose which field on that node to use in the route (the URL) for your pages.
  3. Create a new page component in your src/pages directory using the following naming convention: {nodeType.field}.js.
    • Don’t forget to include the curly braces ({}) in your filenames to indicate the dynamic part of the route!

For example, if you wanted to create a separate page for each Product node, and you wanted to use the product’s name field in the URL, you’d create a new file at src/pages/{Product.name}.js. Then Gatsby would create those pages at routes like /water-bottle or /sweatshirt or /notebook.

In this part of the Tutorial, you’ll use Gatsby’s File System Route API to dynamically create new pages for each of your blog posts.

According to the API, you need to decide on two things before creating a collection route:

  • Which type of node to create pages from.
  • Which field from that node type to use in the URL.

Since your blog posts are written in MDX, you’ll use MDX as the node type to create pages from. But which field on the MDX nodes should you use?

gatsby-plugin-mdx automatically adds a slug field to each MDX node, which contains a string of the filename for the .mdx file (with the .mdx extension removed). You can see the slug field’s value for each MDX node in GraphiQL. If you run the following query:

…you should get back something like the result below:

That looks like a good format for a URL!

Note: In this case, the slug field is a good choice because it’s human readable, which means the URLs for your blog posts will be easier for users to understand. But you can use any field in your routes, even if it contains special characters or whitespace, as Gatsby will “slugify” every route. For example, I ♥ Dogs will be converted to i-love-dogs.

Task: Create blog post page template

Now that you know what node type and field to use, you can plug them into the File System Routes naming convention. To create new pages from the slug field of your MDX nodes, you should make a new file at src/pages/{mdx.slug}.js.

The diagram below shows the different routes that Gatsby will create when it builds your site:

A diagram showing how files in the "src/pages" directory get turned into routes for the site. Extended description below.

Expand for detailed description

When you build your site, Gatsby looks at the page components in your src/pages directory and creates new pages for your site.

  • src/pages/index.js lives at the / route.
  • src/pages/blog.js lives at the /blog route.
  • src/pages/{mdx.slug}.js gets turned into multiple routes - one for each MDX node in the data layer.
    • Gatsby uses the MDX node with slug my-first-post to build a page that lives at the /my-first-post route.
    • Gatsby uses the MDX node with slug another-post to build a page that lives at the /another-post route.
    • Gatsby uses the MDX node with slug yet-another-post to build a page that lives at the /yet-another-post route.
  1. Create a new file in your src/pages directory called {mdx.slug}.js. This will be the file for your blog post page template.

  2. Create a basic page component in your new {mdx.slug}.js file. For now, add in the Layout component, but hard code the page title and page contents. (You’ll make those dynamic later on.)

  3. In a web browser, go to localhost:8000/my-first-post and you should see your hard-coded content. You can update your URL with the slugs for your other posts to check that identical pages were created for them too.

A screenshot of the blog post template page in a web browser. The page title says, "Super Cool Blog Posts," and the post contents have a paragraph element that says, "My blog post contents will go here (eventually)."

Pro Tip: Not sure which pages were created? Check out the 404 page when you run gatsby develop. (You can get to it by trying to access the URL for a page that doesn’t exist.) The bottom of the page lists the routes for all the pages Gatsby created for your site.

(If you’re making changes to your routes, you’ll have to stop and restart your local development server for the list of pages on the 404 page to update.)

A screenshot of the development 404 page in a web browser. The URL is "localhost:8000/fake-route" and the 404 page shows an error message followed by a list of links to existing pages.

Task: Update route to include a /blog path parameter

So far, all of your pages have been created off the root domain of your site (localhost:8000). But it would be better (both for search engine optimization and for general organization) if you grouped all your blog posts under a /blog path parameter. That way, the URLs for all your blog posts would start with localhost:8000/blog/.

Since Gatsby builds page routes based on the folder structure inside the src/pages directory, you can add new path parameters to a page by creating subdirectories inside of src/pages.

The following diagram shows an overview of the updates you’ll have to make in order to add a /blog path parameter to the routes for your blog posts. The process is also outlined in more detail below.

A diagram showing a new folder structure for the page components, along with the corresponding routes that get generated. Extended description below.

Expand for detailed description

When Gatsby builds the pages for your site, it creates routes based on the folder structure of the src/pages directory. So if your src/pages directory contains subdirectories with page components, the name of the subdirectory will get added as a path parameter for those pages.

  • src/pages/index.js still lives at the / route.
  • src/pages/blog/index.js lives at the /blog route.
  • src/pages/blog/{mdx.slug}.js gets turned into multiple routes - one for each MDX node in the data layer.
    • Gatsby uses the MDX node with slug my-first-post to build a page that lives at the blog/my-first-post route.
    • Gatsby uses the MDX node with slug another-post to build a page that lives at the blog/another-post route.
    • Gatsby uses the MDX node with slug yet-another-post to build a page that lives at the blog/yet-another-post route.
  1. Create a new folder in your src/pages directory, and call it blog.

  2. Move the src/pages/{mdx.slug}.js file into the new blog subdirectory. Update the import for your Layout component to reflect the new folder structure:

  3. Once your local development server rebuilds your site, check in a web browser that the paths to your blog posts have updated.

    • For example, you should now have a page at localhost:8000/blog/my-first-post, and trying to access localhost:8000/my-first-post (without the blog path parameter) should send you to the 404 page.

Pro Tip: Gatsby caches information about your site as it builds it, to make subsequent builds faster. But sometimes, when you make changes to your site, you’ll need to empty the cache for your changes to show up.

If you’re seeing unexpected behavior (like maybe your local development server isn’t picking up your new changes), you can run gatsby clean from the command line to delete the cache and start fresh on your next build.

Don’t have the Gatsby CLI globally installed? Try running npx gatsby clean instead.

  1. For organization, it would be nice to keep all your blog-related pages together. Move the src/pages/blog.js file into your new src/pages/blog directory as well.

    • Rename the file from blog.js to index.js. (Otherwise your blog page will move to localhost:8000/blog/blog).

    • You’ll also need to update the import for the Layout component to reflect the new folder structure:

    • You may need to stop and restart your local development server for the changes to be picked up.

  2. In a web browser, check that your Blog page still shows up at localhost:8000/blog.

Nice work! You’ve now used Gatsby’s File System Route API to create pages from nodes in the data layer.

Render post contents in the blog post page template

Now that you’ve got all the pages for your posts set up, it’s time to pull in the actual post contents. You learned in Part 4 that you can pull data into your components using GraphQL queries. But how can you tell Gatsby which MDX node from the data layer to pull into each page? To do that, you’ll need to learn about another key GraphQL concept: query variables.

Key GraphQL Concept: Query Variables

In GraphQL, query variables are a way to send extra data along with your request. With query variables, you can write dynamic queries that return different data based on the values you pass in.

Let’s take a look at an example in GraphiQL. In Part 5, you learned about passing arguments to fields to change the data you get back in the response. For example, you could use the query below to request data for the MDX node that has a slug field equal to "another-post":

…which would return the following response:

In this case, your slug value is hard coded into your GraphQL query. But what happens if you want to swap out a different value on a different page? That’s where query variables come in.

GraphiQL has a collapsible “Query Variables” section at the bottom of the Query Editor pane. If you click on it, a new text area appears, where you can add key-value pairs for data that you want to pass into your query. These key-value pairs should be written in JSON. For example, adding the object below to the Query Variables section passes your request a query variable called slug with a value of another-post:

To use the query variable inside your query, do the following:

  1. Define your query variable. It should include the variable name (with a $ in front of it) and its GraphQL data type.
  2. Use the query variable in your query. (You’ll need to add a $ before the variable name.)

For example, here’s how you would update the previous query to use query variables instead of a field argument:

Running this new query should return the same response as the previous one without query variables. But now, you can swap out the value of your slug variable with a different value, like "my-first-post", and your response will send back the correct node.

The diagram below shows how the query, query variables, and response all fit together in the GraphiQL interface:

A screenshot of GraphiQL in a web browser. The Query Editor pane in the middle shows the request with query variables. The Query Variables pane below shows a JSON object with a single key-value pair for the `slug`. The Result Window shows the single MDX node returned in the response.

Note: In Gatsby, query variables can only be used inside of page queries. (You can’t use them with the useStaticQuery hook.)

When you use Gatsby’s File System Route API, it automatically adds some props into the page template component for each page:

  • The id for the data layer node is used to create the page.
  • The field you used to create the dynamic part of the route. (In this case, the slug field.)

Under the hood, Gatsby makes both of these values available to use as query variables in your page queries.

Want to take a closer look?

  1. Add a console.log statement to print out the props for your BlogPost page component in src/pages/blog/{mdx.slug}.js.
  2. In a web browser, go to localhost:8000/blog/my-first-post and open the developer tools. In the console tab, you should see an object similar to the one below:

The keys in the pageContext object get added when you create a page using the File System Route API. These are also the keys that are available for you to use as query variables in your page query for the blog post page template.

  1. Start by using GraphiQL to create a page query for your blog post page template.
    • Since each page only needs data for a single MDX node, use the mdx field.
    • The fastest way to look up nodes is using the id field, so use the id query variable instead of slug.

Tip: If you want to test out your query in GraphiQL, you’ll need to add an id key to the Query Variables section. You can copy one of the id values from running an allMdx query in GraphiQL.

The JSON object in the Query Variables section should look something like the one below. (You’ll need to use your own id, as copying the one below won’t work.):

  1. Add your page query to the blog post page template.

    • Don’t forget to import the graphql tag!
    • You should also delete the query name MyQuery or replace it with a unique name.
  2. In Part 4, you learned that Gatsby passes in the results from your page query into your page component as a data prop. You can update your BlogPost component to use the data prop and render the contents of your blog post. (Remember to import MDXRenderer so that you can render the body of your post!)

  3. In your web browser, go to one of your blog post pages (like localhost:8000/blog/my-first-post). You should see the contents of your blog post rendered in their own page!

    • Try checking the routes for your other posts, to make sure all your pages are rendering the post contents correctly.

A screenshot of the "my-first-post" blog page in a web browser. The post's title, date, and body contents are rendered.

Update Blog page to link to each post

So far, you’ve used the File System Route API and GraphQL query variables to create separate pages for each of your blog posts.

The last step of Part 6 is to clean up your Blog page. Instead of rendering the full contents of your blog posts, the Blog page should link out to the new post pages you just created.

  1. In your src/pages/blog/index.js file, remove the body field from your JSX and your page query. You can also get rid of the MDXRenderer component.

  2. Add the slug field to your page query, and use it to turn the post title into a link to the post page.

    • Since these links are between pages on your own site, you can use Gatsby’s Link component to get some extra performance benefits.
    • If you use absolute URLs, you’ll need to add the extra /blog/ path parameter, since the slug field only contains the last part of the path (like my-first-post).
  3. In a web browser, go to localhost:8000/blog. Your Blog page should now show links to each of your blog post pages. A screenshot of the Blog page in a web browser. It shows the title and date for each post, and each post title is a link to the page for that post.

Congratulations, you now have a multi-page blog site! Try adding some new .mdx files to your top-level blog directory. They should get added to your Blog page automatically when your site rebuilds!

Want to see how it all fits together? Check out the commit history in the GitHub repo for the finished example site.

Summary

Take a moment to think back on what you’ve learned so far. Challenge yourself to answer the following questions from memory:

  • What is the File System Route API used for?
  • What is the syntax for creating a new collection route?
  • What is a query variable?
  • When can you use a query variable?

Ship It! 🚀

Before you move on, deploy your changes to your live site on Gatsby Cloud so that you can share your progress!

First, run the following commands in a terminal to push your changes to your GitHub repository. (Make sure you’re in the top-level directory for your Gatsby site!)

Once your changes have been pushed to GitHub, Gatsby Cloud should notice the update and rebuild and deploy the latest version of your site. (It may take a few minutes for your changes to be reflected on the live site. Watch your build’s progress from your Gatsby Cloud dashboard.)

Key takeaways

  • Gatsby’s File System Route API lets you dynamically create new pages from data layer nodes by naming your files with a special syntax.
    • File System Routes only work on files in the src/pages directory (or subdirectories).
    • To create a new collection route, you name your file {nodeType.field}.js, where nodeType is the type of node you want to create pages from, and field is the data field from that node type that you want to use in the URL for that page.
  • Query variables let you pass in different data values to the same GraphQL query. They can be combined with field arguments to get back data only about a specific node.
  • Query variables can only be used in page queries.

Share Your Feedback!

Our goal is for this Tutorial to be helpful and easy to follow. We’d love to hear your feedback about what you liked or didn’t like about this part of the Tutorial.

Use the “Was this doc helpful to you?” form at the bottom of this page to let us know what worked well and what we can improve.

What’s coming next?

In Part 7, you’ll revisit gatsby-plugin-image (from way back in Part 3). This time, you’ll learn how to create dynamic images using the GatsbyImage component. You’ll put the finishing touches on your blog site by adding hero images to your blog posts.

Continue to Part 7