skip to content
me nonoakij

Next.js (App Router) で Radix と Tailwind CSS を使う

/ 5 min read

はじめに

本記事では Next.js (App Router) で Radix と Tailwind CSS が動作するところまでをまとめました。 pnpm を使っているので、npm や yarn を使っている場合は適宜読み替えてください。

記載時の各ライブラリのバージョンや環境

Next.js のプロジェクトを作成

pnpm create next-app

以下のように質問されるので、回答します。

 What is your project named? my-next-app
 Would you like to use TypeScript? Yes
 Would you like to use ESLint? Yes
 Would you like to use Tailwind CSS? Yes
 Would you like to use `src/` directory? No
 Would you like to use App Router? (recommended) … Yes
 Would you like to customize the default import alias (@/*)? … No

Radix をインストール

pnpm add @radix-ui/themes

globals.css を変更する

app/globals.css を以下のように変更します。

@tailwind base;
@tailwind components;
@tailwind utilities;
@import "@radix-ui/themes/styles.css"

layout.tsx を変更する

app/layout.tsx を以下のように変更します。 追加したのは、@radix-ui/themes の Theme コンポーネントです。

import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { Theme } from "@radix-ui/themes"

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Theme>{children}</Theme>
      </body>
    </html>
  );
}

[Optional] Radix Themes の Token を Tailwind CSS の Class として使えるようにする

例えば、以下のように Radix Themes で定義されている Token を Tailwind CSS の Class として使いたい場合に有効です。

<button className="bg-accent-9 px-rx-2">Click Me 😘</Button>

radix-themes-tw をインストールします

pnpm add -D radix-themes-tw

tailwind.config.js を変更します

tailwind.config.ts を以下のように変更します。

import type { Config } from "tailwindcss"
import { radixThemePreset } from "radix-themes-tw"

const config: Config = {
  presets: [radixThemePreset],
  content: [
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
}
export default config

client component への対応

Dialog コンポーネントを使う場合、以下のようなエラーが発生することがあります。

Unhandled Runtime Error
Error: Could not find the module "/Users/path/to/project/node_modules/.pnpm/@radix-ui+themes@2.0.3_@types+react-dom@18.2.19_@types+react@18.2.58_react-dom@18.2.0_react@18.2.0/node_modules/@radix-ui/themes/dist/esm/components/dialog.js#Dialog#Root" in the React Client Manifest. This is probably a bug in the React Server Components bundler.

Dialog コンポーネントは Client Component として ‘use client’ directive を使用する必要がありますが、Dialog コンポーネント自体には ‘use client’ directive が含まれていないことが原因です。

今回は、元の使用感を変えないために以下のように対応しました。

components 配下に client.tsx と index.tsx を作成します

components/
└── dialog
    ├── client.tsx
    └── index.tsx

client.tsx

"use client"
import { Dialog } from "@radix-ui/themes"

const Root = Dialog.Root
const Trigger = Dialog.Trigger
const Content = Dialog.Content
const Title = Dialog.Title
const Description = Dialog.Description
const Close = Dialog.Close

export { Root, Trigger, Content, Title, Description, Close }

index.tsx

import {
  Root,
  Trigger,
  Content,
  Title,
  Description,
  Close,
} from "@/components/dialog/client"

export const Dialog = {
  Root,
  Trigger,
  Content,
  Title,
  Description,
  Close,
}

このようにすることで、Server Components から直接 Dialog をコンポーネントを使うことができます。

app/path/to/page.tsx

import { Dialog } from "@/components/dialog"

export default function Home() {
  return (
    <Dialog.Root>
      <Dialog.Trigger>
        <Button variant="solid" color="blue">
          Edit profile
        </Button>
      </Dialog.Trigger>
      <Dialog.Content style={{ maxWidth: 450 }}>
        <Dialog.Title>Edit profile</Dialog.Title>
        <Dialog.Description size="2" mb="4">
          Make changes to your profile.
        </Dialog.Description>
      </Dialog.Content>
    </Dialog.Root>
  )
};

できなかったこと

特定条件で Tailwind CSS の button, [type='button'] に対する Reset CSS が Radix の Button コンポーネントの background-color よりも優先されるため、背景色があたらないケースがあります。

devtool styles pane

Dialog の Trigger などで、type="button"を付与された場合に発生します。

radix-ui/primitives/Dialog.tsx での実装箇所

パッと対応方法がわからなかったので何かわかれば追記します

Reference