Gatsby is a static web site framework that’s based on React.
We can use it to create static websites from external data sources and more.
In this article, we’ll look at how to create a site with Gatsby.
Tags Pages for Blog Posts
We can create tag pages for blog posts.
To do this, we work on a few files:
gatsby-config.js
module.exports = {
plugins: [
`gatsby-transformer-remark`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `content`,
path: `${__dirname}/src/content`,
},
},
],
}
gatsby-node.js
const path = require("path")
const _ = require("lodash")
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const slug = createFilePath({ node, getNode, basePath: `pages` })
createNodeField({
node,
name: `slug`,
value: slug,
})
}
}
exports.createPages = async ({ actions, graphql, reporter }) => {
const { createPage } = actions
const blogPostTemplate = path.resolve("src/templates/post.js")
const tagTemplate = path.resolve("src/templates/tags.js")
const result = await graphql(`
{
postsRemark: allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___date] }
limit: 2000
) {
edges {
node {
fields {
slug
}
frontmatter {
tags
}
}
}
}
tagsGroup: allMarkdownRemark(limit: 2000) {
group(field: frontmatter___tags) {
fieldValue
}
}
}
`)
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
const posts = result.data.postsRemark.edges
posts.forEach(({ node }) => {
const slug = node.fields.slug
createPage({
path: slug,
component: blogPostTemplate,
context: { slug },
})
})
const tags = result.data.tagsGroup.group
tags.forEach(tag => {
createPage({
path: `/tags/${_.kebabCase(tag.fieldValue)}/`,
component: tagTemplate,
context: {
tag: tag.fieldValue,
},
})
})
}
src/templates/tags.js
import React from "react"
import PropTypes from "prop-types"
import { Link, graphql } from "gatsby"
const Tags = ({ pageContext, data }) => {
const { tag } = pageContext
const { edges, totalCount } = data.allMarkdownRemark
const tagHeader = `${totalCount} post${totalCount === 1 ? "" : "s"
} tagged with "${tag}"`
return (
<div>
<h1>{tagHeader}</h1>
<ul>
{edges.map(({ node }) => {
const { slug } = node.fields
const { title } = node.frontmatter
return (
<li key={slug}>
<Link to={slug}>{title}</Link>
</li>
)
})}
</ul>
<Link to="/tags">All tags</Link>
</div>
)
}
Tags.propTypes = {
pageContext: PropTypes.shape({
tag: PropTypes.string.isRequired,
}),
data: PropTypes.shape({
allMarkdownRemark: PropTypes.shape({
totalCount: PropTypes.number.isRequired,
edges: PropTypes.arrayOf(
PropTypes.shape({
node: PropTypes.shape({
frontmatter: PropTypes.shape({
title: PropTypes.string.isRequired,
}),
fields: PropTypes.shape({
slug: PropTypes.string.isRequired,
}),
}),
}).isRequired
),
}),
}),
}
export default Tags
export const pageQuery = graphql`
query($tag: String) {
allMarkdownRemark(
limit: 2000
sort: { fields: [frontmatter___date], order: DESC }
filter: { frontmatter: { tags: { in: [$tag] } } }
) {
totalCount
edges {
node {
fields {
slug
}
frontmatter {
title
}
}
}
}
}
`
src/templates/post.js
import React from "react"
import { graphql } from "gatsby"
export default function BlogPost({ data }) {
const post = data.markdownRemark
return (
<div>
<h1>{post.frontmatter.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</div>
)
}
export const query = graphql`
query($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
html
frontmatter {
title
}
}
}
`
src/content/post.md
---
title: "A Trip To the Zoo"
tags: ["animals", "Chicago", "zoos"]
date: "2020-01-01"
---
I went to the zoo today. It was terrible.
In gatsby-config.js
, we add the gatsby-source-filesystem
plugin to read the Markdown files from the src/content
folder.
And we add the gatsby-transformer-remark
plugin to transform the Markdown to HTML.
Next, in gatsby-node.js
, we create the slug
field with the onCreateNode
method.
We call the createFilePath
function to create the slug
field value from the file path.
Then we call createNodeField
to create the slug
field with the object with name
set to 'slug'
.
The value is the slug string that we created from createFilePath
.
In the createPages
function, we maker the query to get the post data.
The result.data.postRemark.edges
has the posts data.
We call forEach
on it so that we can call createPage
on each entry in the callback to create the pages.
node.field.slug
has the slug value we created from onCreateNode
.
component
is set to the blogPostTemplate
so that we can display the site.
Then to get the tags, we use the result.data.tagsGroup.group
property.
We pass in the slug
to the context
property of in the first forEach
to get that data to the page.
We call forEach
with a callback that calls the createPage
function again.
But this time, we use it to create the tags page.
The path
is the URL for the tags page.
component
has the template we use to render the tags page.
context
has the data we want to render on the tags page.
In tags.js
, we have the tags page template.
We have the pageQuery
object to make the query for the items with the given tag.
Then in the tags
component, we get all the returned data from the data
prop.
edges
have the items with the given tag.
We call map
to render the returned content.
The slug
property has the slug. title
has the title.
We have the Link
component for each entry so we can go to the page with the given slug.
In post.js
, we render the page.
We get the data from the data
prop’s markdownRemark
property.
Then we see the data displayed.
We have the query
object to make the query to get the post with the given slug.
Conclusion
We can add a tags page to display the Markdown file with the given tags with a post page to display the Markdown file with a given slug with Gatsby.