Shadcn/UIとは?初心者向け導入チュートリアル

Audrey Lopez

Audrey Lopez

14 6月 2025

Shadcn/UIとは?初心者向け導入チュートリアル

ウェブ開発者にとって、完璧なUIツールキットの探求は絶え間ない努力です。長年、React開発者はMaterial-UI (MUI)、Ant Design、Chakra UIのような伝統的なコンポーネントライブラリに頼ってきました。これらのライブラリは豊富な事前構築済みコンポーネントを提供し、開発の加速を約束します。しかし、それらはしばしばトレードオフを伴います:制御の欠如、まるで戦いのようなスタイル上書き、そして肥大化したバンドルサイズです。

ここに登場するのがShadcn UIです。これはReactコミュニティに旋風を巻き起こしている、パラダイムを変えるアプローチです。これはあなたが慣れ親しんだようなコンポーネントライブラリではありません。それ以上のものです。これは美しくデザインされ、アクセシブルで、無限に再利用可能なコンポーネントのコレクションであり、npmから依存関係としてインストールするのではなく、プロジェクトに直接コピーして使用します。

この包括的な4000語のチュートリアルは、あなたが完全な初心者から自信を持ってShadcn UIを使いこなせるようになるための決定版ガイドとなります。その基本的な哲学を探求し、詳細なセットアップ手順をたどり、複雑なUIを構築し、高度なテーマ設定とフォーム処理をマスターし、大規模アプリケーションのためのベストプラクティスについて議論します。UIツールキットに何を期待するか、考え直す準備をしてください。

💡
美しいAPIドキュメントを生成する優れたAPIテストツールをお探しですか?

開発チームが最大限の生産性で共同作業できる、統合されたオールインワンプラットフォームをお探しですか?

Apidogはあなたのすべての要求に応え、Postmanをはるかに手頃な価格で置き換えます
ボタン

Shadcn UIの哲学 - 新しい構築方法

コードを一行も書く前に、Shadcn UIがなぜ存在し、どのような問題を解決するのかを理解することが最も重要です。この核となる哲学を把握することが、その可能性を最大限に引き出す鍵となります。

Shadcn UIではないもの

Shadcn UIとは

このモデルの主な利点は、スピードとコントロールの融合です。事前構築済みコンポーネントを使用することによる初期の速度を得ながら、自分のコードを所有することから生まれる長期的な柔軟性と保守性を犠牲にしません。


舞台設定 - プロジェクトのセットアップとインストール

理論から実践に移りましょう。新しいプロジェクトをゼロからセットアップします。このガイドでは、主にNext.jsを使用します。そのサーバーコンポーネントとファイルベースのルーティングは、Shadcn UIの精神と完璧に一致するからです。また、Viteのセットアップについても簡単に触れます。

ステップ1:環境の前提条件

開発環境の準備ができていることを確認してください。以下が必要です:

ステップ2:新しいNext.jsアプリケーションの作成

ターミナルを開き、以下のコマンドを実行して新しいNext.jsプロジェクトをブートストラップします。Bash

npx create-next-app@latest my-pro-shadcn-app --typescript --tailwind --eslint

このコマンドは、my-pro-shadcn-appという名前のディレクトリに新しいアプリケーションのひな形を作成します。いくつかの重要なフラグを含めました:

インストーラーがいくつかの質問をします。これらはモダンなNext.js 14+セットアップのための推奨される選択肢です:

✔ Would you like to use `src/` directory? … No / **Yes**
✔ Would you like to use App Router? (recommended) … No / **Yes**
✔ Would you like to customize the default import alias? … **No** / Yes

App Routerの使用は標準的なプラクティスであり、src/ディレクトリはコードの整理に役立ちます。完了したら、新しいプロジェクトに移動します。Bash

cd my-pro-shadcn-app

ステップ3:initコマンド - Shadcn UIを起動する

これは最も重要なステップです。Shadcn UIはプロジェクトを構成するためのCLIツールを提供します。プロジェクトのルートディレクトリから以下のコマンドを実行します。Bash

npx shadcn-ui@latest init

これにより、プロジェクトをセットアップするための対話式の質問が開始されます。各質問とその重要性を分解してみましょう:

確認後、CLIはその魔法を発揮します:

  1. 依存関係をインストール: tailwindcss-animateclass-variance-authorityなどの必要なパッケージを追加します。
  2. components.jsonを作成: 設定の選択肢を保存します。
  3. tailwind.config.tsを更新: Shadcn UIプラグインとテーマ設定を注入します。
  4. globals.cssを更新: 色パレット全体、ボーダーラジウスなどを定義する大量のCSS変数ブロックを追加します。
  5. lib/utils.tsを作成: このファイルは、Tailwind CSSクラスを条件付きでマージするための巧妙なユーティリティであるcnヘルパー関数をエクスポートします。

これでプロジェクトの構成が完了しました。

(代替:Viteセットアップ)

ReactでViteを使用している場合も、プロセスは非常に似ています。Vite + React + TSプロジェクトをセットアップした後、手動でTailwind CSSをインストールし、npx shadcn-ui@latest initを実行します。CLIはViteセットアップを検出するのに十分賢く、ファイルの位置(例:globals.cssの代わりにindex.css)について少し異なる質問をします。


UIの構築 - シンプルなコンポーネントから複雑なレイアウトまで

セットアップが完了したので、構築を開始しましょう。コアワークフローは、ニーズを特定し、コンポーネントを追加し、それを使用することです。

ステップ4:最初のコンポーネントを追加して使用する

デフォルトのNext.jsボイラープレートをクリーンアップし、シンプルなインターフェースを構築しましょう。

1. ボタンを追加:Bash

npx shadcn-ui@latest add button

何が起こるか見てみましょう:新しいファイルsrc/components/ui/button.tsxが作成されます。これがあなたのボタンです。あなたはそれを所有しています。

2. カードを追加:Bash

npx shadcn-ui@latest add card

このコマンドはより興味深いです。src/components/ui/card.tsxを作成します。このファイルを調べると、複数のコンポーネント(CardCardHeaderCardTitleCardDescriptionCardContentCardFooter)がエクスポートされていることがわかります。これは複合コンポーネントの一般的なパターンです。

3. UIを構築:

さて、src/app/page.tsxを開き、その内容を以下のものに置き換えます。TypeScript

import { Button } from "@/components/ui/button";
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input"; // We'll add this next
import { Label } from "@/components/ui/label";   // And this

export default function Home() {
  return (
    <main className="flex min-h-screen items-center justify-center bg-background p-8">
      <Card className="w-full max-w-md">
        <CardHeader>
          <CardTitle className="text-2xl">Create Project</CardTitle>
          <CardDescription>
            Deploy your new project in one-click.
          </CardDescription>
        </CardHeader>
        <CardContent className="grid gap-4">
          <div className="grid gap-2">
            <Label htmlFor="name">Name</Label>
            <Input id="name" placeholder="Name of your project" />
          </div>
          <div className="grid gap-2">
            <Label htmlFor="framework">Framework</Label>
            {/* We'll replace this with a Select component later */}
            <Input id="framework" placeholder="e.g. Next.js" />
          </div>
        </CardContent>
        <CardFooter>
          <Button className="w-full">Deploy</Button>
        </CardFooter>
      </Card>
    </main>
  );
}

InputコンポーネントとLabelコンポーネントが不足しているため、私たちのコードはまだ実行されません。それらを追加しましょう。Bash

npx shadcn-ui@latest add input
npx shadcn-ui@latest add label

次に、開発サーバーを実行します。Bash

npm run dev

http://localhost:3000にアクセスしてください。カード内にクリーンでプロフェッショナルな見た目のフォームが表示されます。w-fullmax-w-mdgridのようなユーティリティクラスをJSXで直接使用してレイアウトを制御していることに注目してください。これがShadcnとTailwind CSSを組み合わせる力です。

ステップ5:より洗練されたコンポーネントを導入する

静的な入力は良いですが、実際のアプリケーションにはインタラクティブな要素が必要です。フォームを強化しましょう。

1. Selectコンポーネントを追加: 「Framework」入力はドロップダウンであるべきです。Selectコンポーネントを追加しましょう。これはより複雑で、他のコンポーネントに依存しています。Bash

npx shadcn-ui@latest add select

CLIは賢いです。Selectが機能するためにPopoverコンポーネントが必要であることを認識し、それとその依存関係もインストールする許可を求めます。これは、手動で依存関係を追跡する必要がなくなる素晴らしい機能です。

2. Selectコンポーネントを統合: src/app/page.tsxの「Framework」用のInputを新しいSelectコンポーネントに置き換えます。TypeScript

// Add these imports at the top
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";

// ... inside the CardContent
<div className="grid gap-2">
  <Label htmlFor="framework">Framework</Label>
  <Select>
    <SelectTrigger id="framework">
      <SelectValue placeholder="Select a framework" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="nextjs">Next.js</SelectItem>
      <SelectItem value="sveltekit">SvelteKit</SelectItem>
      <SelectItem value="astro">Astro</SelectItem>
      <SelectItem value="nuxt">Nuxt.js</SelectItem>
    </SelectContent>
  </Select>
</div>

ブラウザをリロードしてください。アニメーションと適切なキーボードナビゲーションを備えた、完全に機能しアクセシブルなセレクトドロップダウンが表示されます。これはすべて、Radix UIが内部で動作しているおかげです。

3. Toastでユーザーフィードバックを追加: ユーザーが「Deploy」をクリックしたらどうなるでしょうか?フィードバックを与えるべきです。Toastコンポーネントはこれに最適です。

まず、それを追加します。Bash

npx shadcn-ui@latest add toast

次に、トーストを使用するには、アプリ内のどこにでも表示できるように、ルートレイアウトに<Toaster />コンポーネントを追加する必要があります。src/app/layout.tsxを開き、変更します。TypeScript

import { Toaster } from "@/components/ui/toaster" // Import the Toaster

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Toaster /> {/* Add it here, just before closing body */}
      </body>
    </html>
  )
}

さて、トーストをトリガーする方法が必要です。useToastフックを使用します。src/app/page.tsxをクライアントコンポーネントにして、ボタンクリックを処理するように更新しましょう。TypeScript

'use client'; // <-- Add this at the very top of the file

// ... other imports
import { useToast } from "@/components/ui/use-toast";

export default function Home() {
  const { toast } = useToast(); // Get the toast function from the hook

  function handleDeploy() {
    toast({
      title: "Deployment Scheduled!",
      description: "Your project 'Name of your project' is being deployed.",
      duration: 5000,
    });
  }

  return (
    <main className="flex min-h-screen items-center justify-center bg-background p-8">
      <Card className="w-full max-w-md">
        {/* ... CardHeader and CardContent ... */}
        <CardFooter>
          <Button className="w-full" onClick={handleDeploy}> {/* Add onClick handler */}
            Deploy
          </Button>
        </CardFooter>
      </Card>
    </main>
  );
}

これで、「Deploy」ボタンをクリックすると、画面の隅に洗練された通知が表示されます。


バリデーション付きのプロフェッショナルなフォームを構築する

ほとんどの実際のアプリケーションでは、クライアントサイドのバリデーションを含む堅牢なフォーム処理が必要です。Shadcn UIでこれを処理する公式な方法は、状態管理のためにreact-hook-formと、スキーマバリデーションのためにzodを組み合わせることです。構築してみましょう。

ステップ6:フォームの依存関係をインストールする

まず、必要なライブラリをインストールしましょう。Bash

npm install react-hook-form zod @hookform/resolvers

ステップ7:ShadcnのFormコンポーネントを追加する

Shadcn UIは、react-hook-formをUIコンポーネントとシームレスに接続するためのラッパーとして機能する特別なFormコンポーネントを提供します。Bash

npx shadcn-ui@latest add form

これにより、src/components/ui/form.tsxが追加されます。このファイルは、ボイラープレートを大幅に削減するコンテキスト対応コンポーネント(FormFormFieldFormItemFormLabelFormControlFormDescriptionFormMessage)のセットを提供します。

ステップ8:バリデーションスキーマを作成する

src/app/page.tsxで、zodを使用してフォームデータの形状とルールを定義しましょう。TypeScript

// Add these imports at the top
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form";

さて、Homeコンポーネントのすぐ上にスキーマを作成しましょう。TypeScript

const formSchema = z.object({
  projectName: z.string().min(2, {
    message: "Project name must be at least 2 characters.",
  }).max(50, {
    message: "Project name must not exceed 50 characters.",
  }),
  framework: z.string({
    required_error: "Please select a framework to display.",
  }),
});

このスキーマは2つのフィールドを定義しています:projectNameは2文字から50文字の間の文字列である必要があり、frameworkは必須の文字列です。

ステップ9:フォームを配線する

さて、これらすべての新しいツールを使用するようにHomeコンポーネントをリファクタリングしましょう。TypeScript

export default function Home() {
  const { toast } = useToast();

  // 1. Define your form.
  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      projectName: "",
    },
  });

  // 2. Define a submit handler.
  function onSubmit(values: z.infer<typeof formSchema>) {
    // Do something with the form values.
    // ✅ This will be type-safe and validated.
    console.log(values);
    toast({
      title: "You submitted the following values:",
      description: (
        <pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
          <code className="text-white">{JSON.stringify(values, null, 2)}</code>
        </pre>
      ),
    });
  }

  // 3. Build the JSX with Shadcn's Form components
  return (
    <main className="flex min-h-screen items-center justify-center bg-background p-8">
      <Card className="w-full max-w-md">
        <CardHeader>
          <CardTitle className="text-2xl">Create Project</CardTitle>
          <CardDescription>
            Deploy your new project in one-click.
          </CardDescription>
        </CardHeader>
        <CardContent>
          <Form {...form}>
            <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
              <FormField
                control={form.control}
                name="projectName"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Name</FormLabel>
                    <FormControl>
                      <Input placeholder="Name of your project" {...field} />
                    </FormControl>
                    <FormDescription>
                      This is your public display name.
                    </FormDescription>
                    <FormMessage /> {/* Displays validation errors */}
                  </FormItem>
                )}
              />
              <FormField
                control={form.control}
                name="framework"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Framework</FormLabel>
                    <Select onValueChange={field.onChange} defaultValue={field.value}>
                      <FormControl>
                        <SelectTrigger>
                          <SelectValue placeholder="Select a framework" />
                        </SelectTrigger>
                      </FormControl>
                      <SelectContent>
                        <SelectItem value="nextjs">Next.js</SelectItem>
                        <SelectItem value="sveltekit">SvelteKit</SelectItem>
                        <SelectItem value="astro">Astro</SelectItem>
                        <SelectItem value="nuxt">Nuxt.js</SelectItem>
                      </SelectContent>
                    </Select>
                    <FormDescription>
                      The framework you want to deploy.
                    </FormDescription>
                    <FormMessage />
                  </FormItem>
                )}
              />
              <Button type="submit" className="w-full">Deploy</Button>
            </form>
          </Form>
        </CardContent>
      </Card>
    </main>
  );
}

これはかなりの量のコードですが、信じられないほど強力でスケーラブルなパターンです。FormFieldコンポーネントはすべての状態接続を処理し、FormMessageはユーザーがフィールドを操作したときに、zodスキーマからの正しいバリデーションエラーを自動的に表示します。空のプロジェクト名でフォームを送信して、バリデーションが動作するのを確認してみてください。


テーマ設定とカスタマイズをマスターする

Shadcn UIの真の力は、それを自分自身のものにし始めたときに解き放たれます。

ステップ10:CSS変数による高度なテーマ設定

テーマ全体はsrc/app/globals.cssのCSS変数によって定義されます。このファイルを開き、:root.darkブロックを探してください。CSS

/* Example from globals.css */
:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --primary: 222.2 47.4% 11.2%;
  --primary-foreground: 210 40% 98%;
  /* ... and many more */
  --radius: 0.5rem;
}

.dark {
  --background: 222.2 84% 4.9%;
  --foreground: 210 40% 98%;
  --primary: 210 40% 98%;
  --primary-foreground: 222.2 47.4% 11.2%;
  /* ... */
}

ダークモードの実装:

Shadcnは、.darkクラスブロックとtailwind.config.tsのTailwindのdarkMode: "class"戦略のおかげで、ダークモード用に事前設定されています。必要なのは、<html>要素のdarkクラスを切り替える方法だけです。これに人気のあるライブラリはnext-themesです。

  1. インストール: npm install next-themes
  2. ThemeProviderコンポーネントを作成(src/components/theme-provider.tsx):TypeScript
"use client"
import * as React from "react"
import { ThemeProvider as NextThemesProvider } from "next-themes"
import { type ThemeProviderProps } from "next-themes/dist/types"

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}
  1. このプロバイダーでRootLayoutをラップします(src/app/layout.tsx):TypeScript
import { ThemeProvider } from "@/components/theme-provider"

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          {children}
          <Toaster />
        </ThemeProvider>
      </body>
    </html>
  )
}
  1. 最後に、トグルボタンを作成します(例:src/components/mode-toggle.tsx):TypeScript
"use client"
import * as React from "react"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
import { Button } from "@/components/ui/button"

export function ModeToggle() {
  const { theme, setTheme } = useTheme()

  return (
    <Button
      variant="outline"
      size="icon"
      onClick={() => setTheme(theme === "light" ? "dark" : "light")}
    >
      <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
      <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
      <span className="sr-only">Toggle theme</span>
    </Button>
  )
}

これで、この<ModeToggle />をアプリ内のどこにでも配置して、システム対応でユーザーが上書き可能なダークモードトグルを取得できます。

ステップ11:コンポーネントのソースコードをカスタマイズする

これが究極のスーパーパワーです。緑色の背景を持つボタンの新しい成功バリアントが必要だとしましょう。

src/components/ui/button.tsxを開きます。buttonVariantsの定義を見つけてください。これはcva(Class Variance Authority)を使用しています。新しいバリアントを追加するだけです。TypeScript

const buttonVariants = cva(
  // ... base styles
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
        outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
        secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
        success: "bg-green-600 text-white hover:bg-green-600/90", // Our new variant
      },
      // ... size variants
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

それだけです。これでコードで使用できます: <Button variant="success">Success</Button>。複雑なCSSの上書きを書く必要はありませんでした。コンポーネント自身のソースコードを編集しただけです。このワークフローはシンプルで予測可能であり、信じられないほど強力です。


パート6:ベストプラクティスと今後の展望

アプリケーションが成長するにつれて、留意すべきベストプラクティスがいくつかあります。

結論:あなたがライブラリの著者です

これで、Shadcn UIの核となる哲学から、高度な現実世界のパターンを実装するまでを旅しました。その真の革新はコンポーネント自体だけでなく、それが表すパラダイムシフトであることを見てきました。それは開発者をライブラリの単なる消費者から、自身のUIツールキットのキュレーターおよび所有者へと移行させます。

生のソースコードを提供し、Tailwind CSSとRadix UIの堅牢な基盤の上に構築し、シームレスなCLIエクスペリエンスを提供することで、Shadcn UIは初期開発速度と長期的な保守性および創造的な自由との間の完璧なバランスを実現しています。あなたはもはや他人のデザインシステムに制約されません。プロジェクト内のコンポーネントはあなた自身のものです—変更し、拡張し、完璧にすることができます。

アプリケーションのUIの未来は、もはやサードパーティの依存関係の手にあるのではなく、componentsフォルダの中にあります。楽しく構築してください。

💡
美しいAPIドキュメントを生成する優れたAPIテストツールをお探しですか?

開発チームが最大限の生産性で共同作業できる、統合されたオールインワンプラットフォームをお探しですか?

Apidogはあなたのすべての要求に応え、Postmanをはるかに手頃な価格で置き換えます
ボタン

ApidogでAPIデザイン中心のアプローチを取る

APIの開発と利用をよりシンプルなことにする方法を発見できる