2021 June 7thCase insensitive tags in Gatsbygatsby, graphql
How to create case insensitive tags in Gatsby.
Graphql has no way to handle case insensitivity at the moment. So case insensitivity for my tags frontmatter became an issue. This Github thread elaborates on the issue.
One of the suggested solutions there is to use the Gatsby plugin gatsby-plugin-node-fields. This is a guide for that solution.
1. Install the plugin
npm install --save gatsby-plugin-node-fields
2. Set the plugin up in gatsby-node
The plugin can either be configured as a plugin in gatsby-config, or as a function in your gatsby-node. I prefer the latter to keep all node-manipulation concerns in one file.
First import the plugin:
// gatsby-node.js
const { attachFields } = require(`gatsby-plugin-node-fields`)
Next is to setup the descriptor. Important to understand what a predicate
is.
Each descriptor must provide a
predicate
- a function that will be passed the node and decides whether the descriptor should be used to transform it. For example we might want to check if the node is Markdown node, or if it represents a file from a particular directory.
In my case, it's to check if the node type is Mdx
.
// gatsby-node.js
const { attachFields } = require(`gatsby-plugin-node-fields`)
const descriptors = [
{
predicate: node => node.internal.type === "Mdx",
fields: [
{
name: "tagsFormatted",
getter: node => node.frontmatter.tags,
defaultValue: "",
transformer: value =>
isEmptyString(value) ? [] : tagsToLowerCase(value),
},
],
},
]
The fields
key is more straightforward:
name
is the name of the fieldgetter
gets the values from the specified node (here we are getting the values of our tags frontmatter)defaultValue
supplies default value where value on the node isundefined
transformer
is where the magic happens. It transforms our frontmatter tags to lower case with our customtagsToLowerCase
function. If there are no tags for the node, it returns an empty array.
The final code looks like this:
// gatsby-node.js
const { attachFields } = require(`gatsby-plugin-node-fields`)
// lower case array
const tagsToLowerCase = tagsArr => {
const output = tagsArr.map(tag => tag.toLowerCase())
return output
}
const isEmptyString = value => {
if (value === "") {
return true
}
return false
}
const descriptors = [
{
predicate: node => node.internal.type === "Mdx",
fields: [
{
name: "tagsFormatted",
getter: node => node.frontmatter.tags,
defaultValue: "",
transformer: value =>
isEmptyString(value) ? [] : tagsToLowerCase(value),
},
],
},
]
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `Mdx`) {
const value = createFilePath({ node, getNode })
createNodeField({
node,
name: `slug`,
value: `/blog/posts${value}`,
})
}
// plugin-node-fields
attachFields(node, actions, getNode, descriptors)
}
3. Query for your new field
Once it's setup, a new tagsFormatted
node will appear under fields
. You can then just query for it. This is how it looks like in my project:
// gatsby-node.js
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const result = await graphql(`
query {
allMdx {
edges {
node {
id
fields {
slug
}
}
}
}
tagsGroup: allMdx(limit: 2000) {
group(field: fields___tagsFormatted) {
fieldValue
}
}
}
`)
if (result.errors) {
reporter.panicOnBuild('🚨 ERROR: Loading "createPages" query')
}
// Create tags pages
const tags = result.data.tagsGroup.group
tags.forEach(tag => {
createPage({
path: `/tags/${_.kebabCase(tag.fieldValue)}`,
component: path.resolve(`./src/components/tagsTemplate.js`),
context: {
tag: tag.fieldValue,
},
})
})
}
fields___tagsFormatted
values will all be lowercase.