Add a class on the <body> on a certain page?
Unanswered
dperolio posted this in #help-forum
dperolioOP
Using Next 14 app dir, how would you go about adding a class onto the body or html, for a specific page.tsx?
35 Replies
what are you actually trying to accomplish. explain your end goal, the reason for the class, and what it does to the rest of your site
simple way would be to use useEffect together with usePathname()
and lil bit of basic js
dperolioOP
Let's say I want to style the body differently on a particular page.
I tried importing a stylesheet in the page.tsx, but it seems those styles persist even when switching to a different page. And you can't use a generic body tag in a module css file.
@<Milind ツ /> simple way would be to use useEffect together with usePathname()
dperolioOP
I don't really follow. You can't use useEffect in an RSC, right?
yea but a nested client component can
dperolioOP
Create a new component just to add a class to the body? 

what type of style are you trying to apply?
any example
dperolioOP
In this particular case
body {
overflow-x: hidden;
background: linear-gradient(var(--color-blue-darken-20) 0%,var(--color-blue) 50.74%,var(--color-blue) 100%),url(/images/home-bg.jpg) no-repeat center/cover;
background-blend-mode: multiply;
background-attachment: fixed;
}so different background color for 2 different routes?
dperolioOP
Right.
dont think we can do this without client component. i might be wrong
you absolutely can.
route groups with separate root layouts.
more complex css.
moving body lower than root layout
route groups with separate root layouts.
more complex css.
moving body lower than root layout
lots of options.
i did think of route groups but where would we import the css? in the route group layout or parent layout?
oh so we can redeclare body tag in route group
interesting
totally distinct html trees
dperolioOP
Would React Context be another option?
@dperolio Would React Context be another option?
client shenanigans are always an option. just not always the right/best option
dperolioOP
I guess. It just seems a little wasteful and redundant to create a new folder and 2 files just to add a class to the body to change the background.
Maybe I'll just make a ContentWrapper or Body client component and have that be the body where I can pass props to it. The downside being I'll have to use that as the wrapper in every page, but seems like it makes more sense to me.
You probably could just use useEffect instead of making a wrapper and using context
And have it imported in root layout.
dperolioOP
Wrapper is more versatile though.
It allows me to pass other props when/if needed, to the body, as well as header and footer, and whatever else I want to include in the wrapper, which would normally just be directly in the layout and unable to be passed props to.
You could also add a
data-path="..." property to the body. Then in your CSS use that with body[data-path=""] to apply route-specific styles.@Paul. You could also add a `data-path="..."` property to the body. Then in your CSS use that with `body[data-path=""]` to apply route-specific styles.
dperolioOP
How do you get the path though? You would have to create a client component and use useEffect as Milind was suggesting, right?
@dperolio How do you get the path though? You would have to create a client component and use useEffect as Milind was suggesting, right?
You are using the app router right? Because then you could make a component like this:
Then in your layout use that
'use client';
export const Body: FC<PropsWithChildren> = ({ children }) => {
const pathname = usePathname();
return (
<body data-path={pathname}>{children}</body>
}Then in your layout use that
Body instead of the existing bodydperolioOP
I'm reading about things like
import { headers } from "next/headers";
const heads = headers()
const pathname = heads.get('next-url') being a possible option, but discouraged. Also not getting reliable results with it so far.Or you could indeed add middleware where you add a custom header with the url based on the incoming request. Then read that header in your layout.
Both could work 🤷
Both could work 🤷
The middleware would look like
and a util function looking like:
export async function middleware(request: NextRequest): Promise<NextResponse> {
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-url', request.url);
return NextResponse.next({
request: {
headers: requestHeaders
}
})
}and a util function looking like:
export function getRouteInfo(): {
pathname: string;
} {
const map = headers();
const url = new URL(map.get('x-url') ?? '');
let pathname = url.pathname;
return {
pathname,
};
}