Creating tag index pages with Gatsby
Oct 08, 2023
I've been giving Gatsby a trial run in this new version of my site. When using GitHub pages, one of the things I'd always longed for was dynamic tag "index" pages for my blog posts.
Tag post list template
The first step is to create a template to render a list of posts for a given tag. The component for this is quite simple:
const TagPostListTemplate = (props: TagTemplateProps) => {
const posts = props.data.allMarkdownRemark.edges;
return (
<Layout>
<ul>
{posts.map(({ node }) => (
<BlogPostPreview
key={node.id}
slug={node.frontmatter.slug}
title={node.frontmatter.title}
tags={node.frontmatter.tags}
excerpt={node.excerpt}
/>
))}
</ul>
</Layout>
);
};
The props
will be populated from a GraphQL query, and to make things easier I'm using the same BlogPostPreview
component that I use on the homepage.
The GraphQL query is quite simple, too. My tags are stored in the frontmatter of the markdown pages, and we can use a filter
clause to check that the $tag
query variable is in the tags
listed against each post.
export const query = graphql`
query($tag: String!) {
allMarkdownRemark(
sort: { frontmatter: { date: DESC } },
filter: { frontmatter: { tags: { in: [$tag] } } }
) {
edges {
node {
excerpt(format: HTML)
id
frontmatter { title, slug, tags }
}
}
}
}
`;
Instantiating the pages
We now have a template capable of listing all posts matching a given tag, so the next step is to use Gatsby's page generation methods to create an instance of this template for each of the extant tags.
This can be handled within gatsby-node.js
in a createPages
function (you can read the docs about this here). The first step is to query all existing tags via GraphQL.
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions;
const result = await graphql(`
{
allMarkdownRemark {
edges {
node {
frontmatter {
tags
}
}
}
}
}
`);
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`);
return;
}
const posts = result.data.allMarkdownRemark.edges;
We now have access to all posts in the system, and all tags on those posts. Let's create an array of unique tags.
const tags = Array.from(
new Set(
result.data.allMarkdownRemark.edges.flatMap(
edge => edge.node.frontmatter.tags
)
)
);
Now it's as simple as using the Gatsby createPage
action, and using the template we created earlier as reference.
const tagTemplate = path.resolve(`src/templates/TagPostListTemplate.tsx`);
tags.forEach(tag => {
createPage({
path: `/tags/${_.kebabCase(tag)}/`,
component: tagTemplate,
context: {
tag,
},
});
});
Finishing up
After running a Gatsby build, a number of /tags/
pages will be created for each tag in the system. Try clicking on one of the tags against this blog post to see how the filtering works.
Exercises for the reader
- Link to the
/tags/
index page when tags are clicked - Add a way to clear selected tag
- Add a highlight to the currently selected tag (I used React Context for this)