You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Remix中为body添加随页面动态变化的类名?

问题

需要给root.jsx中的<body>标签添加随页面变化的类名,要求每个路由页面手动指定类名:

  • 首页(_index.jsx)类名为company homepage
  • 餐厅页(restaurants.jsx)类名为venue company

之前尝试通过loader从API获取pageClassName数据设置类名,但部分页面无该数据,因此需要从路由页面手动传递类名参数到root.jsx。相关代码如下:

root.jsx

export default function App() {
  return (
    <html lang='en'>
      <head>
        <meta charSet='utf-8' />
        <meta name='viewport' content='width=device-width, initial-scale=1' />
        <link rel='preconnect' href='https://fonts.googleapis.com' />
        <link
          rel='preconnect'
          href='https://fonts.gstatic.com'
          crossOrigin='anonymous'
        />
        <link
          href='https://fonts.googleapis.com/css?family=Montserrat:wght@100,300,400,500,700&display=swap'
          rel='stylesheet'
        />
        <Meta />
        <Links />
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}

_index.jsx

export default function Index() {
  const { startPage } = useLoaderData()
  const page = startPage.page.sections;
  return (
    <main id='main'>
      <PageComponents page={page} />
    </main>
  );
}

restaurants.jsx

export default function Restaurants() {
  const { restaurants } = useLoaderData();
  return (
    <>
      <PageComponents page={restaurants.sections} />
      <footer className='footer' />
    </>
  );
}

PageComponents.jsx

import loadable from "@loadable/component";

const components = {
  logo: loadable(() => import("./Logo")),
  intro: loadable(() => import("./Intro")),
  languageSwitch: loadable(() => import("./LanguageSwitch")),
  venueGroup: loadable(() => import("./VenueGroup")),
};

export default function PageComponents({ page }) {
  return (
    <main>
      {page
        .filter(({ type }) => !!components[type])
        .map(({ data, type }) => {
          const Component = components[type];

          return (
            <div key={type}>
              <Component data={data} />
            </div>
          );
        })}
    </main>
  );
}
解决方案

提供两种可行实现方式,按需选择:


方式一:通过Outlet上下文传递类名(页面级单独控制)

利用React Router的useOutletContext能力,让每个页面主动传递类名给根组件:

  1. 修改root.jsx
    引入useOutletContext获取页面传递的类名,绑定到<body>标签:
import { useOutletContext } from "react-router-dom";

export default function App() {
  const pageClassName = useOutletContext() || "";
  return (
    <html lang='en'>
      <head>
        <meta charSet='utf-8' />
        <meta name='viewport' content='width=device-width, initial-scale=1' />
        <link rel='preconnect' href='https://fonts.googleapis.com' />
        <link
          rel='preconnect'
          href='https://fonts.gstatic.com'
          crossOrigin='anonymous'
        />
        <link
          href='https://fonts.googleapis.com/css?family=Montserrat:wght@100,300,400,500,700&display=swap'
          rel='stylesheet'
        />
        <Meta />
        <Links />
      </head>
      <body className={pageClassName}>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}
  1. 修改首页(_index.jsx)
    添加类名传递逻辑:
import { useOutletContext } from "react-router-dom";

export default function Index() {
  const { startPage } = useLoaderData()
  const page = startPage.page.sections;
  useOutletContext("company homepage");
  return (
    <main id='main'>
      <PageComponents page={page} />
    </main>
  );
}
  1. 修改餐厅页(restaurants.jsx)
    同样添加类名传递:
import { useOutletContext } from "react-router-dom";

export default function Restaurants() {
  const { restaurants } = useLoaderData();
  useOutletContext("venue company");
  return (
    <>
      <PageComponents page={restaurants.sections} />
      <footer className='footer' />
    </>
  );
}

方式二:路由配置中统一管理类名(集中维护)

在路由配置里给每个路由添加meta字段存储类名,根组件通过路由匹配获取类名:

  1. 修改路由配置文件(示例:routes.jsx)
import { createBrowserRouter } from "react-router-dom";
import Index from "./_index";
import Restaurants from "./restaurants";

export const router = createBrowserRouter([
  {
    path: "/",
    element: <Index />,
    meta: { pageClassName: "company homepage" }
  },
  {
    path: "/restaurants",
    element: <Restaurants />,
    meta: { pageClassName: "venue company" }
  }
]);
  1. 修改root.jsx
    引入路由配置,匹配当前路由获取类名:
import { useLocation, matchRoutes } from "react-router-dom";
import { router } from "./routes";

export default function App() {
  const location = useLocation();
  const matchedRoute = matchRoutes(router.routes, location)[0]?.route;
  const pageClassName = matchedRoute?.meta?.pageClassName || "";

  return (
    <html lang='en'>
      <head>
        <meta charSet='utf-8' />
        <meta name='viewport' content='width=device-width, initial-scale=1' />
        <link rel='preconnect' href='https://fonts.googleapis.com' />
        <link
          rel='preconnect'
          href='https://fonts.gstatic.com'
          crossOrigin='anonymous'
        />
        <link
          href='https://fonts.googleapis.com/css?family=Montserrat:wght@100,300,400,500,700&display=swap'
          rel='stylesheet'
        />
        <Meta />
        <Links />
      </head>
      <body className={pageClassName}>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}

这种方式无需修改页面组件,类名统一在路由配置中维护,适合路由较多的场景。


内容的提问来源于stack exchange,提问作者Viktoria Tarasova

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 13:20:40