React Router

React RouterのプロジェクトにYamada UIをインストールして使用するためのガイド。

インストール

  • 1

    アプリケーションを作成する

    React Routerのアプリケーションを作成します。

    pnpm create react-router my-app
    
  • 2

    セットアップする

    コマンドを実行すると、プロジェクトに必要なファイルやフォルダが作成されます。

    pnpm dlx @yamada-ui/cli init
    
  • 3

    パッケージをインストールする

    アプリケーションに@workspaces/uiをインストールします。

    pnpm add "@workspaces/ui@workspace:*"
    
  • 4

    プロバイダーを追加する

    インストール後、アプリケーションのルートにUIProviderを追加します。 ハイドレーションエラーを抑制するために、htmlbodysuppressHydrationWarningtrueに設定します。

    root.tsx

    import { Meta, Outlet, Scripts, ScrollRestoration } from "react-router"
    import { UIProvider } from "@workspaces/ui"
    
    export function Layout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="en" suppressHydrationWarning>
          <head>
            <meta charSet="utf-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <Meta />
          </head>
          <body suppressHydrationWarning>
            <UIProvider>{children}</UIProvider>
            <ScrollRestoration />
            <Scripts />
          </body>
        </html>
      )
    }
    
    export default function App() {
      return <Outlet />
    }
    
  • 5

    コンポーネントを使用する

    UIProviderを追加したら、アプリケーション内でコンポーネントを使用します。

    home.tsx

    import { Button } from "@workspaces/ui"
    
    export default function Home() {
      return <Button>Click me!</Button>
    }
    

    これで、Yamada UIのセットアップは完了です!

スクリプト

ColorModeScript

カラーモードを使用する場合は、正常に動作させるためにbodyColorModeScriptを追加する必要があります。

理由は、カラーモードがlocalStoragecookiesを用いて実装されており、ページの読み込み時に同期を正しく機能させるためです。

root.tsx

import {
  data,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
  useRouteLoaderData,
} from "react-router"
import { ColorModeScript, UIProvider } from "@workspaces/ui"
import type { Route } from "./+types/root"

export async function loader({ request }: Route.LoaderArgs) {
  const cookie = request.headers.get("cookie") ?? ""
  return data({ cookie })
}

export function Layout({ children }: { children: React.ReactNode }) {
  const { cookie } = useRouteLoaderData("root")

  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
      </head>
      <body suppressHydrationWarning>
        <ColorModeScript type="cookie" />

        <UIProvider cookie={cookie}>{children}</UIProvider>
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  )
}

export default function App() {
  return <Outlet />
}

もし、コンフィグdefaultColorModeを変更した場合は、ColorModeScriptdefaultValueを設定します。

root.tsx

import {
  data,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
  useRouteLoaderData,
} from "react-router"
import { ColorModeScript, UIProvider } from "@workspaces/ui"
import type { Route } from "./+types/root"
import { config } from "@workspace/theme"

export async function loader({ request }: Route.LoaderArgs) {
  const cookie = request.headers.get("cookie") ?? ""
  return data({ cookie })
}

export function Layout({ children }: { children: React.ReactNode }) {
  const { cookie } = useRouteLoaderData("root")

  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
      </head>
      <body suppressHydrationWarning>
        <ColorModeScript type="cookie" defaultValue={config.defaultColorMode} />

        <UIProvider config={config} cookie={cookie}>
          {children}
        </UIProvider>
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  )
}

export default function App() {
  return <Outlet />
}

ThemeSchemeScript

テーマスキームによる画面のフラッシュを抑制したい場合は、body内にThemeSchemeScriptを追加します。

テーマスキームはlocalStoragecookiesを用いて実装されており、ThemeSchemeScriptを追加することでページの読み込み時に同期を正しく機能させることができます。

root.tsx

import {
  data,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
  useRouteLoaderData,
} from "react-router"
import { ThemeSchemeScript, UIProvider } from "@workspaces/ui"
import type { Route } from "./+types/root"

export async function loader({ request }: Route.LoaderArgs) {
  const cookie = request.headers.get("cookie") ?? ""
  return data({ cookie })
}

export function Layout({ children }: { children: React.ReactNode }) {
  const { cookie } = useRouteLoaderData("root")

  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
      </head>
      <body suppressHydrationWarning>
        <ThemeSchemeScript type="cookie" />

        <UIProvider cookie={cookie}>{children}</UIProvider>
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  )
}

export default function App() {
  return <Outlet />
}

もし、コンフィグdefaultThemeSchemeを変更した場合は、ThemeSchemeScriptdefaultValueを設定します。

root.tsx

import {
  data,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
  useRouteLoaderData,
} from "react-router"
import { ThemeSchemeScript, UIProvider } from "@workspaces/ui"
import type { Route } from "./+types/root"
import { config } from "@workspace/theme"

export async function loader({ request }: Route.LoaderArgs) {
  const cookie = request.headers.get("cookie") ?? ""
  return data({ cookie })
}

export function Layout({ children }: { children: React.ReactNode }) {
  const { cookie } = useRouteLoaderData("root")

  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
      </head>
      <body suppressHydrationWarning>
        <ThemeSchemeScript
          type="cookie"
          defaultValue={config.defaultThemeScheme}
        />

        <UIProvider config={config} cookie={cookie}>
          {children}
        </UIProvider>
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  )
}

export default function App() {
  return <Outlet />
}

コンポーネントの統合

React RouterのLinkなどのコンポーネントとYamada UIのコンポーネントを統合することができます。

import type {
  ButtonProps,
  HTMLRefAttributes,
  IconButtonProps,
  LinkProps,
  Merge,
} from "@yamada-ui/react"
import type { FC } from "react"
import type { LinkProps as OriginalLinkProps } from "react-router"
import { Button, IconButton, Link } from "@yamada-ui/react"
import { Link as OriginalLink } from "react-router"

export interface RouterLinkProps
  extends Omit<Merge<OriginalLinkProps, LinkProps>, "as" | "ref"> {}

export const RouterLink: FC<RouterLinkProps> = (props) => {
  return <Link as={OriginalLink} {...props} />
}

export interface RouterLinkButtonProps
  extends Omit<Merge<OriginalLinkProps, ButtonProps>, "as" | "ref">,
    HTMLRefAttributes<"a"> {}

export const RouterLinkButton: FC<RouterLinkButtonProps> = (props) => {
  return <Button as={OriginalLink} {...props} />
}

export interface RouterLinkIconButtonProps
  extends Omit<Merge<OriginalLinkProps, IconButtonProps>, "as" | "ref">,
    HTMLRefAttributes<"a"> {}

export const RouterLinkIconButton: FC<RouterLinkIconButtonProps> = (props) => {
  return <IconButton as={OriginalLink} {...props} />
}
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd