Next.js Discord

Discord Forum

Add a class on the <body> on a certain page?

Unanswered
dperolio posted this in #help-forum
Open in Discord
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
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()
I don't really follow. You can't use useEffect in an RSC, right?
yea but a nested client component can
Create a new component just to add a class to the body? :FeelsCringeManW:
what type of style are you trying to apply?
any example
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?
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
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
yep
interesting
totally distinct html trees
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
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.
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.
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:

'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 body
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 🤷
The middleware would look 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,
  };
}