mcpField
S
shadcn-ui

shadcn-ui

github.com/giuseppe-trisciuoglioデザイン・UI36522.4k 回インストール7 週間前に更新
リポジトリ

shadcn-ui について

shadcn-ui は、React プロジェクトでアクセシブルかつカスタマイズ可能な UI を構築するためのコンポーネントパターンガイドです。コンポーネントのコードをプロジェクトにコピーして編集でき、Radix UI と Tailwind CSS、CSS 変数を活用します。

主な機能

  1. 01コンポーネントのコードをコピーして編集
  2. 02Radix UI プリミティブを基盤に採用
  3. 03Tailwind CSS によるスタイリング
  4. 04CSS 変数によるテーマ設定
  5. 05フォーム、ダイアログ、チャートのパターン

活用シーン

  1. 01プロジェクトにボタン、入力欄、ダイアログを追加
  2. 02React Hook Form と Zod で入力検証付きフォームを構築
  3. 03CSS 変数のテーマとダークモードを設定
  4. 04テーマ対応のチャートを作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill shadcn-ui

インストール後、Claude Code で/shadcn-uiを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
chart.md
charts-components.md
customization.md
forms-and-validation.md
learn.md
nextjs-integration.md
official-ui-reference.md
reference.md
setup-and-configuration.md
ui-components.md
ui-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 によるチャート」「棒グラフ」「折れ線グラフ」「円グラフ」

クイックリファレンス

利用可能なコンポーネント
コンポーネントインストールコマンド説明
buttonnpx shadcn@latest add buttonバリエーション:default、destructive、outline、secondary、ghost、link
inputnpx shadcn@latest add inputテキスト入力フィールド
formnpx shadcn@latest add formバリデーションに対応した React Hook Form との連携
cardnpx shadcn@latest add cardヘッダー、コンテンツ、フッターを備えたコンテナー
dialognpx shadcn@latest add dialogモーダルオーバーレイ
sheetnpx shadcn@latest add sheetスライドオーバーパネル(上/右/下/左)
selectnpx shadcn@latest add selectドロップダウン選択メニュー
toastnpx shadcn@latest add toast通知トースト
tablenpx shadcn@latest add tableデータテーブル
menubarnpx shadcn@latest add menubarデスクトップスタイルのメニューバー
chartnpx shadcn@latest add chartテーマ設定に対応した Recharts のラッパー
textareanpx shadcn@latest add textarea複数行のテキスト入力
checkboxnpx shadcn@latest add checkboxチェックボックス入力
labelnpx 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 で @ エイリアスを設定してください

参考資料

詳しいパターンやコード例については、以下のファイルを参照してください。

コメント