I want to add my custom class to some pages. for example
all pages must be this class fixed-header
exception this routes:
/cart/step-1 /login
this class add or remove to body element.
<body className="bg-gray fixed-header"
but I don’t know how I can handle this scenario?
Advertisement
Answer
Create a custom _document.js
and _app.js
in your pages directory.
A small util to check if class exists on body (to avoid duplicate class, thanks to suggestion by @juliomalves):
// ./utils/hasClasses const hasClasses = () => document.body.classList.contains("bg-gray") && document.body.classList.contains("fixed-header"); export default hasClasses;
Server Side rendering
In _document.js
, use the __NEXT_DATA__
prop to get access to the current page,
check if the page is in your allowed routes, and add the classes to body.
import Document, { Html, Head, Main, NextScript } from "next/document"; class MyDocument extends Document { // Add more routes here if you want to use the same classes allowedRoutes = ["/login", "/cart/step-1"]; getColor() { const { page } = this.props?.__NEXT_DATA__; if (this.allowedRoutes.includes(page)) return "bg-gray fixed-header"; return ""; } render() { return ( <Html> <Head /> <body className={this.getColor()}> <Main /> <NextScript /> </body> </Html> ); } } export default MyDocument;
The above code always runs on the server. Classes doesn’t get appended to the body on client-side navigation.
Client side rendering
To fix the above issue, use the same logic in _app.js
in a useEffect
, so that it adds the correct class when rendering on the client.
import { useEffect } from "react"; import { useRouter } from "next/router"; import "../styles.css"; import hasClasses from "./utils/hasClasses"; function MyApp({ Component, pageProps }) { const { pathname: page } = useRouter(); const allowedRoutes = ["/login", "/cart/step-1"]; useEffect(() => { if (!hasClasses() && allowedRoutes.includes(page)) document.body.className += "bg-gray fixed-header"; else if (hasClasses()) { // Don't want the styles in other pages, remove if exists. // Up to your implementation on how you want to handle this. document.body.classList.remove("bg-gray"); document.body.classList.remove("fixed-header"); } }); return <Component {...pageProps} />; } export default MyApp;
This solves the issue where client side navigation correctly applies the class on the allowed route. The code in _document.js
makes sure that when a page is server rendered, it is sent downstream with the correct class applied so that it doesn’t cause a flash of incorrect styles on the client.