root.tsx

概要

このファイルは必須です

「root」route (app/root.tsx) は、すべての route の親であり、root の <html> ドキュメントのレンダリングを担当するため、React Router アプリケーションで唯一 必須 の route です。

app/root.tsx
import { Outlet, Scripts } from "react-router";
 
import "./global-styles.css";
 
export default function App() {
  return (
    <html lang="en">
      <head>
        <link rel="icon" href="/favicon.ico" />
      </head>
      <body>
        <Outlet />
        <Scripts />
      </body>
    </html>
  );
}

レンダリングするコンポーネント

root route はドキュメントを管理するため、React Router が提供するいくつかの「ドキュメントレベル」のコンポーネントをレンダリングするのに適切な場所です。これらのコンポーネントは root route 内で一度使用され、ページが適切にレンダリングされるために React Router が見つけ出した、または構築したすべてを含みます。

app/root.tsx
import {
  Outlet,
  Scripts,
  ScrollRestoration,
} from "react-router";
 
export default function App() {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
      </head>
      <body>
        {/* 子 route はここにレンダリングされます */}
        <Outlet />
 
        {/* クライアントサイドのトランジションでのスクロール位置を管理します */}
        {/* スクリプトに nonce ベースのコンテンツセキュリティポリシーを使用する場合、`nonce` prop を指定する必要があります。それ以外の場合は、ここに示されているように nonce prop を省略してください。 */}
        <ScrollRestoration />
 
        {/* スクリプトタグはここに入ります */}
        {/* スクリプトに nonce ベースのコンテンツセキュリティポリシーを使用する場合、`nonce` prop を指定する必要があります。それ以外の場合は、ここに示されているように nonce prop を省略してください。 */}
        <Scripts />
      </body>
    </html>
  );
}

React 19 を使用していない場合、または React の <link><title>、および <meta> コンポーネントを使用しないことを選択し、代わりに React Router の links および meta export に依存している場合は、root route に以下を追加する必要があります。

app/root.tsx
import { Links, Meta } from "react-router";
 
export default function App() {
  return (
    <html lang="en">
      <head>
        {/* すべての route の `meta` export はここにレンダリングされます */}
        <Meta />
 
        {/* すべての route の `link` export はここにレンダリングされます */}
        <Links />
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

Layout Export

root route はすべての route module export をサポートしています。

root route は、追加のオプションの Layout export もサポートしています。Layout コンポーネントには 2 つの目的があります。

  1. root コンポーネント、HydrateFallback、および ErrorBoundary 全体でドキュメントの「app shell」が重複するのを避ける
  2. root コンポーネント / HydrateFallback / ErrorBoundary 間を切り替えるときに React が app shell 要素を再マウントするのを防ぐ。これにより、React が <Links> コンポーネントから <link rel="stylesheet"> タグを削除して再度追加すると、FOUC が発生する可能性があります。

Layout は単一の children prop を受け取ります。これは default export (例: App)、HydrateFallback、または ErrorBoundary のいずれかです。

app/root.tsx
export function Layout({ children }) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
        <Meta />
        <Links />
      </head>
      <body>
        {/* children は、root Component、ErrorBoundary、または HydrateFallback になります */}
        {children}
        <Scripts />
        <ScrollRestoration />
      </body>
    </html>
  );
}
 
export default function App() {
  return <Outlet />;
}
 
export function ErrorBoundary() {}

Layout コンポーネントにおける useLoaderData に関する注意点

useLoaderDataErrorBoundary コンポーネントでの使用が許可されていません。これは、正常な route レンダリングを意図しており、その型定義には loader が正常に実行され何かを返したという組み込みの前提があるためです。loader がエラーをスローして境界をトリガーした可能性があるため、ErrorBoundary ではその前提は成り立ちません!ErrorBoundary で loader data にアクセスするには、loader data が undefined である可能性を考慮する useRouteLoaderData を使用できます。

Layout コンポーネントは成功フローとエラーフローの両方で使用されるため、同じ制限が適用されます。Layout 内で、それが成功したリクエストであるかどうかに応じてロジックを分岐させる必要がある場合は、useRouteLoaderData("root")useRouteError() を使用できます。

<Layout> コンポーネントは ErrorBoundary のレンダリングに使用されるため、レンダリングエラーなしで ErrorBoundary をレンダリングできるように 非常に注意深く 確認する必要があります。Layout が境界をレンダリングしようとして別のエラーをスローすると、使用できなくなり、UI は非常に最小限の組み込みのデフォルト ErrorBoundary にフォールバックします。

app/root.tsx
export function Layout({
  children,
}: {
  children: React.ReactNode;
}) {
  const data = useRouteLoaderData("root");
  const error = useRouteError();
 
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
        <Meta />
        <Links />
        <style
          dangerouslySetInnerHTML={{
            __html: `
              :root {
                --themeVar: ${
                  data?.themeVar || defaultThemeVar
                }
              }
            `,
          }}
        />
      </head>
      <body>
        {data ? (
          <Analytics token={data.analyticsToken} />
        ) : null}
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}