Add Tags & Categories to Gatsby MDX blog
Tags and categories are an important section of a website, since they provide an easy way for visitors to browse content on your platform.
To add tags to your Gatsby MDX blog posts, you need to have your site set up to convert MDX files into blog posts (I wrote about it here).
Add tags to your MDX files
You can add tags in the frontmatter
of your MDX file:
1---2slug: '/add-tags-and-categories-to-gatsby-mdx-blog'3date: '2020-08-23'4title: '🕺 Add Tags & Categories to Gatsby MDX blog'5tags: ['js', 'react', 'gatsby']6---
The frontmatter
is the top area of your Markdown or MDX file that is surrounded by dashes.
It includes the title of your post, a date and any other field that is a string, number, or array.
Considering that posts can have multiple tags, I have defined the above tag field as an array.
Write a query to get all tags for your posts
Now that the tags
fields are available in the data layer, we can use graphql
to query it.
Try running the following query, which groups posts by tags
, locally in your GraphiQL (http://localhost:8000/___graphql
):
1{2 tagsGroup: allMdx(limit: 2000) {3 group(field: frontmatter___tags) {4 fieldValue5 }6 }7}
Then, modify your gatsby-node.js
file with the above query:
1const path = require('path');2const _ = require('lodash');34exports.createPages = async ({ actions, graphql, reporter }) => {5 const { createPage } = actions;67 const tagTemplate = path.resolve('src/templates/tagTemplate.js');89 const result = await graphql(`10 {11 tagsGroup: allMdx(limit: 2000) {12 group(field: frontmatter___tags) {13 fieldValue14 }15 }16 }17 `);1819 // ...2021 // Extract tag data from query22 const tags = result.data.tagsGroup.group;23 // Make tag pages24 tags.forEach(tag => {25 createPage({26 path: `/tags/${_.kebabCase(tag.fieldValue)}/`,27 component: tagTemplate,28 context: {29 tag: tag.fieldValue,30 },31 });32 });33};
For reference, here is my entire gatsby-node.js
file.
Make a tags page template
As you can see on the gatsby-node.js
file, we need a tag page template in order to generate individual pages for the tags in your posts.
Let's create a tag template for /tags/{tag}
:
1import React from 'react';2import { Link, graphql } from 'gatsby';34import Layout from '../components/layout';5import SEO from '../components/seo';67export const query = graphql`8 query($tag: String) {9 allMdx(10 limit: 200011 sort: { fields: [frontmatter___date], order: DESC }12 filter: { frontmatter: { tags: { in: [$tag] } } }13 ) {14 totalCount15 edges {16 node {17 excerpt18 frontmatter {19 title20 slug21 date(formatString: "MMMM DD, YYYY")22 tags23 }24 }25 }26 }27 }28`;2930const Tags = ({ pageContext, data }) => {31 const { tag } = pageContext;32 const { edges, totalCount } = data.allMdx;33 const tagHeader = `${totalCount} post${34 totalCount === 1 ? '' : 's'35 } tagged with "${tag}"`;3637 return (38 <Layout>39 <SEO title={`"${tag}" tag`} />4041 <h1>{tagHeader}</h1>4243 <div>44 <Link to="/tags">View all tags</Link>4546 {edges.map(({ node }) => {47 const { excerpt } = node;48 const { slug, title, date, tags } = node.frontmatter;4950 return (51 <div key={slug}>52 <Link to={`/blog${slug}`}>53 <h3>{title}</h3>54 </Link>5556 <p>57 {date}58 <span> ● Tag: </span>59 {tags.map(tag => (60 <Link61 key={tag.toLowerCase()}62 to={`/tags/${tag.toLowerCase()}`}63 >64 {tag}65 </Link>66 ))}67 </p>6869 <p>{excerpt}</p>70 </div>71 );72 })}73 </div>74 </Layout>75 );76};7778export default Tags;
Make a tags index page
Finally, once we've generated pages for every tag, it's time to create a tags index page.
This page will render a list of all tags (src/pages/tags.js
), followed by the number of posts with that particular tag.
1import React from 'react';2import { Link, graphql } from 'gatsby';3import kebabCase from 'lodash/kebabCase';45import Layout from '../components/layout';6import SEO from '../components/seo';78export const query = graphql`9 query {10 allMdx(limit: 2000) {11 group(field: frontmatter___tags) {12 fieldValue13 totalCount14 }15 totalCount16 }17 }18`;1920const TagsPage = ({21 data: {22 allMdx: { group, totalCount },23 },24}) => (25 <Layout>26 <SEO title="Tags" />2728 <div>29 <h1>Tags</h1>30 <p>31 {totalCount} posts are listed in {group.length} categories32 </p>33 {group.map(({ fieldValue, totalCount }) => (34 <Link key={fieldValue} to={`/tags/${kebabCase(fieldValue)}`}>35 {fieldValue} ({totalCount})36 </Link>37 ))}38 </div>39 </Layout>40);4142export default TagsPage;
You can have a look at how tags work in Gatsby directly from my personal website repo on GitHub.