shadcn-ui について
shadcn-ui は、React プロジェクトでアクセシブルかつカスタマイズ可能な UI を構築するためのコンポーネントパターンガイドです。コンポーネントのコードをプロジェクトにコピーして編集でき、Radix UI と Tailwind CSS、CSS 変数を活用します。
主な機能
- 01コンポーネントのコードをコピーして編集
- 02Radix UI プリミティブを基盤に採用
- 03Tailwind CSS によるスタイリング
- 04CSS 変数によるテーマ設定
- 05フォーム、ダイアログ、チャートのパターン
活用シーン
- 01プロジェクトにボタン、入力欄、ダイアログを追加
- 02React Hook Form と Zod で入力検証付きフォームを構築
- 03CSS 変数のテーマとダークモードを設定
- 04テーマ対応のチャートを作成
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill shadcn-uiインストール後、Claude Code で/shadcn-uiを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/chart.mdcharts-components.mdcustomization.mdforms-and-validation.mdlearn.mdnextjs-integration.mdofficial-ui-reference.mdreference.mdsetup-and-configuration.mdui-components.mdui-reference.md詳細
shadcn/ui コンポーネントのパターン
shadcn/ui、Radix UI、Tailwind CSS を使って、アクセシブルでカスタマイズ可能な UI コンポーネントを構築します。
概要
- コンポーネントはプロジェクトにコピーして使用するため、コードを所有し、カスタマイズできます
- Radix UI のプリミティブを基盤としており、高いアクセシビリティを実現します
- Tailwind CSS と CSS 変数を使ってスタイルを設定し、テーマを適用します
- CLI ベースでインストールできます:
npx shadcn@latest add <component>
使用する場面
次のようなリクエストがあった場合に使用します:
- 「shadcn/ui をセットアップ」「shadcn を初期化」「shadcn コンポーネントを追加」
- 「button/input/form/dialog/card/select/toast/table/chart をインストール」
- 「React Hook Form」「Zod によるバリデーション」「バリデーション付きフォーム」
- 「アクセシブルなコンポーネント」「Radix UI」「Tailwind のテーマ」
- 「shadcn の button」「shadcn の dialog」「shadcn の sheet」「shadcn の table」
- 「ダークモード」「CSS 変数」「カスタムテーマ」
- 「Recharts によるチャート」「棒グラフ」「折れ線グラフ」「円グラフ」
クイックリファレンス
利用可能なコンポーネント
| コンポーネント | インストールコマンド | 説明 |
|---|---|---|
button | npx shadcn@latest add button | バリエーション:default、destructive、outline、secondary、ghost、link |
input | npx shadcn@latest add input | テキスト入力フィールド |
form | npx shadcn@latest add form | バリデーションに対応した React Hook Form との連携 |
card | npx shadcn@latest add card | ヘッダー、コンテンツ、フッターを備えたコンテナー |
dialog | npx shadcn@latest add dialog | モーダルオーバーレイ |
sheet | npx shadcn@latest add sheet | スライドオーバーパネル(上/右/下/左) |
select | npx shadcn@latest add select | ドロップダウン選択メニュー |
toast | npx shadcn@latest add toast | 通知トースト |
table | npx shadcn@latest add table | データテーブル |
menubar | npx shadcn@latest add menubar | デスクトップスタイルのメニューバー |
chart | npx shadcn@latest add chart | テーマ設定に対応した Recharts のラッパー |
textarea | npx shadcn@latest add textarea | 複数行のテキスト入力 |
checkbox | npx shadcn@latest add checkbox | チェックボックス入力 |
label | npx shadcn@latest add label | アクセシブルなフォームラベル |
手順
プロジェクトを初期化する
# New Next.js project
npx create-next-app@latest my-app --typescript --tailwind --eslint --app
cd my-app
npx shadcn@latest init
# Existing project
npm install tailwindcss-animate class-variance-authority clsx tailwind-merge lucide-react
npx shadcn@latest init
# Install components
npx shadcn@latest add button input form card dialog select toast
基本的なコンポーネントの使用方法
// Button with variants and sizes
import { Button } from "@/components/ui/button"
<Button variant="default">Default</Button>
<Button variant="destructive" size="sm">Delete</Button>
<Button variant="outline" disabled>Loading...</Button>
Zod によるバリデーション付きフォーム
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { Button } from "@/components/ui/button"
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
const formSchema = z.object({
email: z.string().email("Invalid email"),
password: z.string().min(8, "Password must be at least 8 characters"),
})
export function LoginForm() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: { email: "", password: "" },
})
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(console.log)} className="space-y-4">
<FormField name="email" control={form.control} render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl><Input type="email" {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
<FormField name="password" control={form.control} render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl><Input type="password" {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
<Button type="submit">Login</Button>
</form>
</Form>
)
}
複数フィールドを含む高度なフォーム、API 送信に対応したお問い合わせフォーム、ログインカードのパターンについては、references/forms-and-validation.md を参照してください。
ダイアログ(モーダル)
import { Button } from "@/components/ui/button"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Open</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Edit Profile</DialogTitle>
</DialogHeader>
{/* content */}
</DialogContent>
</Dialog>
トースト通知
// 1. Add <Toaster /> to app/layout.tsx
import { Toaster } from "@/components/ui/toaster"
// 2. Use in components
import { useToast } from "@/components/ui/use-toast"
const { toast } = useToast()
toast({ title: "Success", description: "Changes saved." })
toast({ variant: "destructive", title: "Error", description: "Something went wrong." })
棒グラフ
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
import { ChartContainer, ChartTooltipContent } from "@/components/ui/chart"
const chartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
} satisfies import("@/components/ui/chart").ChartConfig
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart data={data}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<ChartTooltip content={<ChartTooltipContent />} />
</BarChart>
</ChartContainer>
折れ線グラフ、面グラフ、円グラフの例については、references/charts-components.md を参照してください。
例
バリデーション付きログインフォーム
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { Button } from "@/components/ui/button"
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
const formSchema = z.object({
email: z.string().email("Invalid email"),
password: z.string().min(8, "Min 8 characters"),
})
export function LoginForm() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: { email: "", password: "" },
})
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(console.log)} className="space-y-4">
<FormField name="email" control={form.control} render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl><Input type="email" {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
<FormField name="password" control={form.control} render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl><Input type="password" {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
<Button type="submit">Login</Button>
</form>
</Form>
)
}
アクション付きデータテーブル
import { ColumnDef } from "@tanstack/react-table"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { DataTable } from "@/components/ui/data-table"
const columns: ColumnDef<User>[] = [
{ id: "select", header: ({ table }) => (
<Checkbox checked={table.getIsAllPageRowsSelected()} />
), cell: ({ row }) => (
<Checkbox checked={row.getIsSelected()} />
)},
{ accessorKey: "name", header: "Name" },
{ accessorKey: "email", header: "Email" },
{ id: "actions", cell: ({ row }) => (
<Button variant="ghost" size="sm">Edit</Button>
)},
]
フォーム付きダイアログ
import { Button } from "@/components/ui/button"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Add User</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Add New User</DialogTitle>
</DialogHeader>
{/* <LoginForm /> */}
</DialogContent>
</Dialog>
トースト通知
import { useToast } from "@/components/ui/use-toast"
import { Button } from "@/components/ui/button"
const { toast } = useToast()
toast({ title: "Saved", description: "Changes saved successfully." })
toast({ variant: "destructive", title: "Error", description: "Failed to save." })
ベストプラクティス
- アクセシビリティ:Radix UI のプリミティブを使用します。ARIA 属性は組み込み済みです
- クライアントコンポーネント:インタラクティブなコンポーネント(フック、イベント)には
"use client"を追加します - 型安全性:フォームのバリデーションには TypeScript と Zod スキーマを使用します
- テーマ設定:一貫したデザインのため、
globals.cssで CSS 変数を設定します - カスタマイズ:コンポーネントのファイルを直接変更します。コードは自分で所有します
- パスエイリアス:
tsconfig.jsonで@エイリアスが設定されていることを確認します - レジストリのセキュリティ:信頼できるレジストリからのみコンポーネントをインストールし、本番環境で使用する前に生成されたコードを確認します
- ダークモード:CSS 変数の方式と
next-themesを使って設定します - フォーム:常に
Form、FormField、FormItem、FormLabel、FormMessageをまとめて使用します - Toaster:ルートレイアウトに
<Toaster />を一度だけ追加します
制約と警告
- NPM パッケージではありません:コンポーネントはプロジェクトにコピーされるため、バージョン管理された依存関係ではありません
- レジストリのセキュリティ:
npx shadcn@latest addで追加するコンポーネントはリモートから取得されます。インストールする前に、レジストリの提供元が信頼できることを必ず確認してください - クライアントコンポーネント:ほとんどのインタラクティブなコンポーネントでは、
"use client"ディレクティブが必要です - Radix の依存関係:すべての
@radix-uiパッケージがインストールされていることを確認してください - Tailwind が必要です:コンポーネントは Tailwind CSS のユーティリティに依存します
- パスエイリアス:インポート用に
tsconfig.jsonで@エイリアスを設定してください
参考資料
詳しいパターンやコード例については、以下のファイルを参照してください。
- references/setup-and-configuration.md — インストール、tsconfig、tailwind の設定、CSS 変数の全般
- references/ui-components.md — Button、Input、Card、Dialog、Sheet、Select、Toast、Table、Menubar
- references/forms-and-validation.md — React Hook Form + Zod、高度なフォーム、ログインカード、お問い合わせフォーム
- references/charts-components.md — ChartContainer とテーマ設定を使用した棒グラフ、折れ線グラフ、面グラフ、円グラフ
- references/nextjs-integration.md — App Router、サーバー/クライアントコンポーネント、ダークモード、メタデータ
- references/customization.md — カスタムバリアント、CSS 変数、cn() ユーティリティ、コンポーネントの拡張
コメント
Markdown 対応