How to Add Dynamic Page Specific Body Class in Gatsby
Use case: you have a class option in your CMS that should be the class on your Gatsby site's <body>
element. You want it to be added on build time instead of using useEffect on the client side. This is how you add it! There are two versions: one for pre-Gatsby v5.5 and one for older versions.
Gatsby v5.5 or newer versions
Good news! In early 2023, Gatsby v5.5 just added a better way of adding the body tag. Now you can do it within Gatsby's Head API! Here's an example:
1export function Head() {2 return (3 <>4 <html className="example-class" />5 <body className="example-class" />6 <title>Hello World</title>7 </>8 )9}
That Head API code will add an "example-class" to both your body and your html elements. Thank you, Gatsby, that is much better than the previous way!
If you're unfamiliar with the Head API, here's the Head API documentation.
Older Gatsby versions
Here's how you can add the body class on older Gatsby versions!
Add the custom class on your gatsby-node.js
file as a page context in your createPage
function:
1query.data.pages.edges.forEach((loop) => {2 createPage({3 component: Page,4 path: loop.node.slug,5 context: {6 slug: loop.node.slug,7 custom_prop: 'hello-world'8 },9 })10})
Notice the "custom_prop" on the context in the above code block. You probably want to use the actual CMS prop there instead of a static string (so something like a loop.node.bodyClass
in this context).
Now let's use this custom prop on the gatsby-ssr.js
file. If the file does not exist on your project, add it to your root folder (below the gatsby-node.js file).
1exports.onRenderBody = ({ pathname, setBodyAttributes , loadPageDataSync }) => {2 if (process.env.NODE_ENV !== 'development') {3 const { result: { pageContext: { custom_prop } }, } = loadPageDataSync(pathname);4 setBodyAttributes({ className: custom_prop });5 }6}
Note: this will only run on the production build (gatsby build), not the development environment (gatsby develop).
If you are not familiar with the gatsby-ssr file, don't worry, you don't need to add any other code inside it, that's the whole file you need.
Now your body class is being added on build time, which means that it should bring zero javascript to client-side! The class exists in the initial HTML that your visitors will receive.
If you need the class on your development environment, you could try temporarily adding it via useEffect in addition. Here's how you can do that inside your page template:
1import React, { useEffect } from "react"2
3const PageTemplate = ({ pageContext }) => {4
5 useEffect(() => {6 document.body.classList.add(pageContext.custom_prop);7 }, []);8
9 ...
This code uses the pageContext prop to add a body class via useEffect on the client side. It runs once on every page load.
All of this should work in the latest Gatsby v5 version.