Dynamic page generation in GatsbyJS
Tags: gatsby, graphql, pagegeneration, personalsite
Published: 2021-06-29 22:53:36

---
title: Dynamic page generation in GatsbyJS
published: true
date: 2021-06-23 00:00:00 UTC
tags: GatsbyJS,GraphQL,PageGeneration,PersonalSite
series: Building a developer blog with GatsbyJS
canonical_url: https://MihaiBojin.com/personal-site/dynamic-page-generation-gatsbyjs
---

![Photo: Water, splashing](https://MihaiBojin.com/static/18f735e8bfc5226128600fad4e290165/1118c/isaac-sloman-QBYf1HLyrhI-unsplash.jpg)<figcaption>"Photo by <a href="https://unsplash.com/@isaac_slo?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">isaac sloman</a> on <a href="https://unsplash.com/s/photos/dynamic?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a>"</figcaption>

🔔 This article was originally posted on my site, [MihaiBojin.com](https://MihaiBojin.com/personal-site/dynamic-page-generation-gatsbyjs?utm_source=DevTo&utm_medium=organic&utm_campaign=rss "MihaiBojin.com"). 🔔

* * *

Lately, I've been working hard on generating content for my site. So naturally, it falls into multiple categories.

I found myself copy-pasting pages and changing titles, descriptions, and GraphQL queries, a bit too much for my liking.

I didn't feel inspired!

And decided to do something better...

Here's what I did to dynamically generate category pages using a common template.

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#create-category-metadata-in-gatsby-configjs)Create category metadata in gatsby-config.js

First, I defined my categories in `gatsby-config.js`'s `siteMetadata` array.

```
  "categories": [
    {
      "title": "Blog",
      "href": "/blog",
      "description": "My blog. I write about my journey as a software engineering lead and content creator.",
      "pageHeading": "From the blog",
      "pageSubtitle": "Explore some of my blog posts as I embark on a journey to build my site from scratch using GatsbyJS and TailwindUI."
    },
    ...
  ]
```

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#dynamically-create-pages-in-gatsby-nodejs)Dynamically create pages in gatsby-node.js

Then, I extended Gatsby's page creation API:

```
exports.createPages = async ({ graphql, actions, reporter }) => {
  await createCategoryPages({ graphql, actions, reporter }); // a new async function I created
};
```

I split various functionality by context. I defined one function responsible for creating categories.

```
async function createCategoryPages({ graphql, actions, reporter }) {
  const { createPage } = actions;

  // logic here, see below
}
```

I loaded the site's metadata in the function's body; I needed information about my categories and the author.

```
  const result = await graphql(
    `
      {
        site {
          siteMetadata {
            categories {
              title
              href
              description
              pageHeading
              pageSubtitle
            }
            author {
              name
              href
            }
          }
        }
      }
    `,
  );
```

Next, a validation step. I have skipped the validation code, but basically, it's a null check on these objects, followed by a call to `reporter.warn` or `reporter.panicOnBuild`.

```
  const siteMetadata = result.data.site?.siteMetadata;
  const categories = siteMetadata?.categories;
  const author = siteMetadata?.author;
```

Since the component doesn't change, I cached it before iterating.

```
  const component = path.resolve(`./src/components/category.js`);
```

Finally, I iterated through all my categories to create each page.

```
  categories.forEach((page) => {
    createPage({
      path: page.href,
      component,
      context: {
        ...page,
        author,
      },
    });
  });
```

Note: the `component` will need to load a list of articles for each category. Since the site has articles in multiple categories, we need to pass something to construct the correct query. In Gatsby, this is achieved by passing data via the page's context. GraphQL page queries can make use of any [variables passed by context](https://www.gatsbyjs.com/docs/how-to/querying-data/page-query/#how-to-add-query-variables-to-a-page-query).

## [<svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>](#using-a-variable-to-filter-articles-by-category)Using a variable to filter articles by category

For my site, I chose to filter by each category's title. I could have easily filtered by the category's href, but I don't plan on changing either much. Time will tell if this was the right choice...

I updated the old query:

```
query {
    allMarkdownRemark(
        sort: { fields: [frontmatter___date], order: DESC }
        filter: {
          frontmatter: {
            category: {
              title: { eq: "A category title" }
            }
          }
        }
    ) {
        nodes {
        ...
        }
    }
}
```

by giving it a name and passing the `$title` variable.

This variable gets automatically populated from the page's context - 🪄 **magic** 🪄!

```
query CategoryQuery($title: String!) {
    allMarkdownRemark(
        sort: { fields: [frontmatter___date], order: DESC }
        filter: {
          frontmatter: {
            category: {
              title: { eq: $title }
            }
          }
        }
    ) {
        nodes {
        ...
        }
    }
}
```

All I had left to do was to load the page's data and populate my template:

```
export default function Page({ data, pageContext }) {
  const {
    title,
    description,
    pageHeading,
    pageSubtitle,
    author,
  } = pageContext;
  const posts = data.allMarkdownRemark.nodes;
  return (
    <>
        ...
    </>
  );
}
```

Et voila! Dynamically generated pages:

- [Blog](https://mihaibojin.com/blog?utm_source=DevTo&utm_medium=organic&utm_campaign=rss "Mihai Bojin's Blog")
- [Coding Puzzles](https://mihaibojin.com/coding-puzzles?utm_source=DevTo&utm_medium=organic&utm_campaign=rss "Coding Puzzles on Mihai Bojin's Blog")
- [Individual Contributors](https://mihaibojin.com/ic?utm_source=DevTo&utm_medium=organic&utm_campaign=rss "Articles for Individual Contributors on Mihai Bojin's Blog")

* * *

If you liked this article and want to read more like it, [please subscribe to my newsletter](https://motivated-founder-807.ck.page/db1cf284bf "newsletter link"); I send one out every few weeks!