T-CREATOR

shadcn/ui CLI 完全活用:add/update のカスタムテンプレート運用術

shadcn/ui CLI 完全活用:add/update のカスタムテンプレート運用術

shadcn/ui を使ったプロジェクトで、毎回同じカスタマイズを手作業で行っていませんか?CLI の add や update コマンドは便利ですが、デフォルト設定のままでは、プロジェクト固有のスタイルやディレクトリ構成に合わせた調整が毎回必要になってしまいます。

この記事では、shadcn/ui CLI のカスタムテンプレート機能を最大限に活用し、チーム開発でも一貫性のあるコンポーネント管理を実現する方法をご紹介します。components.json の詳細な設定から、実践的な運用テクニックまで、すぐに使える知識をお届けしますね。

背景

shadcn/ui とは

shadcn/ui は、Tailwind CSS と Radix UI をベースにした、コピー&ペースト型の UI コンポーネントライブラリです。従来の npm パッケージとして配布される UI ライブラリとは異なり、必要なコンポーネントのソースコードを直接プロジェクトにコピーする方式を採用しています。

この設計思想により、以下のメリットが得られます。

  • コンポーネントの完全なカスタマイズが可能
  • バンドルサイズの最適化(使用するコンポーネントのみ)
  • バージョン管理の柔軟性
  • TypeScript による型安全性

CLI の基本的な役割

shadcn/ui CLI は、コンポーネントの追加と更新を自動化するツールです。主に 2 つのコマンドで構成されています。

下図は、shadcn/ui CLI がプロジェクトとコンポーネントレジストリの間でどのように動作するかを示しています。

mermaidflowchart TB
    dev["開発者"] -->|"init コマンド"| cli["shadcn/ui CLI"]
    dev -->|"add コマンド"| cli
    dev -->|"update コマンド"| cli

    cli -->|"設定ファイル生成"| config["components.json"]
    cli -->|"コンポーネント取得"| registry["shadcn/ui<br/>レジストリ"]

    registry -->|"テンプレート適用"| cli
    cli -->|"ファイル生成/更新"| project["プロジェクト<br/>components/"]

    config -.->|"設定参照"| cli

図で理解できる要点:

  • CLI は components.json の設定を参照してコンポーネントを生成
  • レジストリから取得したテンプレートに設定を適用
  • プロジェクト内の指定ディレクトリにファイルを配置

CLI を使うことで、手作業でのコピー&ペーストや依存関係の管理から解放され、開発効率が大幅に向上するでしょう。

components.json の重要性

components.json は、shadcn/ui CLI の動作を制御する中核的な設定ファイルです。このファイルに定義された内容に基づいて、コンポーネントのスタイル、配置場所、インポートパスなどが決定されます。

初期化時に npx shadcn-ui@latest init コマンドで生成されますが、プロジェクトの成長に合わせてカスタマイズすることで、真の力を発揮します。

課題

デフォルト設定の限界

shadcn/ui CLI をデフォルト設定のまま使用すると、いくつかの課題に直面することになります。

パス構成の不一致

多くのプロジェクトでは、独自のディレクトリ構成を採用しています。例えば以下のような構成です。

  • src​/​features​/​ ごとにコンポーネントを配置
  • app​/​ ディレクトリ構成(Next.js App Router)
  • モノレポ環境での packages​/​ui​/​ 配置

デフォルトの components​/​ui​/​ への配置では、プロジェクトの構成と合わず、毎回手動でファイルを移動する必要が生じます。

スタイルのカスタマイズ作業

プロジェクト固有のデザインシステムを持っている場合、以下の調整が毎回必要になるでしょう。

  • カラーパレットの変更
  • フォントファミリーの適用
  • ボーダー半径の統一
  • アニメーション設定の調整

これらを add コマンド実行後に手作業で修正すると、時間がかかるだけでなく、修正漏れのリスクも高まります。

チーム開発での一貫性の欠如

複数の開発者が関わるプロジェクトでは、各自が異なる設定でコンポーネントを追加してしまう可能性があります。

mermaidflowchart LR
    A["開発者 A"] -->|"デフォルト設定"| compA["Button.tsx<br/>(スタイル A)"]
    B["開発者 B"] -->|"手動カスタマイズ"| compB["Card.tsx<br/>(スタイル B)"]
    C["開発者 C"] -->|"別の設定"| compC["Dialog.tsx<br/>(スタイル C)"]

    compA --> issue["一貫性のない<br/>コードベース"]
    compB --> issue
    compC --> issue

図で理解できる要点:

  • 各開発者が異なる方法でコンポーネントを追加
  • 結果として統一感のないコードベースが生成される
  • メンテナンス性が低下

更新時の問題

update コマンドを使ってコンポーネントを更新する際にも課題があります。

既存のカスタマイズが上書きされてしまうため、再度手動で修正が必要になります。これは特に以下のケースで問題となるでしょう。

  • プロジェクト固有のバリアント追加
  • アクセシビリティ対応の追加実装
  • パフォーマンス最適化のためのカスタマイズ

また、update が必要なコンポーネントを特定するのも手作業となり、見落としが発生しやすくなります。

解決策

components.json の完全カスタマイズ

components.json を適切に設定することで、上記の課題はすべて解決できます。ここでは、設定ファイルの各項目を詳しく見ていきましょう。

基本構造の理解

まず、components.json の基本構造を確認します。

typescript{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "default",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "app/globals.css",
    "baseColor": "slate",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  }
}

この設定ファイルの各項目が、コンポーネント生成時の動作を決定します。

スタイル設定のカスタマイズ

style プロパティでは、コンポーネントの基本的な見た目を選択します。

json{
  "style": "default"
}

利用可能な値:

  • default: 標準的なスタイル(丸みのあるデザイン)
  • new-york: よりシャープでモダンなスタイル

プロジェクトのデザイントーンに合わせて選択しましょう。一度設定すれば、すべてのコンポーネントに一貫したスタイルが適用されます。

Tailwind CSS 統合の最適化

tailwind セクションでは、Tailwind CSS との統合を細かく制御できます。

json{
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "app/globals.css",
    "baseColor": "zinc",
    "cssVariables": true,
    "prefix": "tw-"
  }
}

各プロパティの役割:

#プロパティ説明
1configTailwind 設定ファイルのパスtailwind.config.ts
2cssグローバル CSS ファイルのパスsrc​/​styles​/​globals.css
3baseColorベースカラーパレットslate, zinc, neutral など
4cssVariablesCSS 変数を使用するかtrue / false
5prefixクラス名のプレフィックスtw-, ui- など

baseColor の選択基準

baseColor は、コンポーネントの中性色を決定する重要な設定です。プロジェクトの雰囲気に合わせて選びましょう。

  • slate: やや青みがかったグレー(モダンな印象)
  • gray: 純粋なグレー(ニュートラル)
  • zinc: わずかに暖かみのあるグレー(落ち着いた印象)
  • neutral: バランスの取れたグレー
  • stone: 茶色がかったグレー(温かみのある印象)

cssVariables の活用

cssVariables を true に設定すると、CSS 変数ベースのテーマシステムが使用されます。これにより、ダークモードの実装が容易になるでしょう。

css/* globals.css */
@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --primary: 221.2 83.2% 53.3%;
    --primary-foreground: 210 40% 98%;
  }

  .dark {
    --background: 222.2 84% 4.9%;
    --foreground: 210 40% 98%;
    --primary: 217.2 91.2% 59.8%;
    --primary-foreground: 222.2 47.4% 11.2%;
  }
}

これらの CSS 変数は、コンポーネント内で自動的に参照されるため、テーマの切り替えがシームレスに機能します。

エイリアス設定の戦略的活用

aliases セクションは、インポートパスの管理において最も重要な設定です。プロジェクト構成に合わせて適切に設定することで、コード品質が大幅に向上します。

基本的なエイリアス設定

json{
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  }
}

これらのエイリアスは、生成されるコンポーネント内のインポート文に使用されます。

プロジェクト構成別の設定例

Next.js App Router を使用する場合の設定例です。

json{
  "aliases": {
    "components": "@/app/_components",
    "utils": "@/lib/utils",
    "ui": "@/app/_components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  }
}

モノレポ環境での設定例を見ていきましょう。

json{
  "aliases": {
    "components": "@repo/ui/components",
    "utils": "@repo/ui/lib/utils",
    "ui": "@repo/ui/components/ui",
    "lib": "@repo/ui/lib",
    "hooks": "@repo/ui/hooks"
  }
}

tsconfig.json との連携

エイリアスを機能させるには、tsconfig.json にも同じパスマッピングを定義する必要があります。

json{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@/components/*": ["./src/components/*"],
      "@/lib/*": ["./src/lib/*"],
      "@/hooks/*": ["./src/hooks/*"]
    }
  }
}

この設定により、TypeScript の型チェックとオートコンプリートが正しく動作するでしょう。

RSC とファイル形式の制御

React Server Components(RSC)と TypeScript/JavaScript の選択も、components.json で制御できます。

RSC の有効化

json{
  "rsc": true
}

rsc を true に設定すると、コンポーネントに "use client" ディレクティブが適切に追加されます。Next.js 13+ の App Router を使用している場合は、true に設定することを推奨します。

ファイル形式の選択

json{
  "tsx": true
}

tsx プロパティで、生成されるファイルの拡張子を制御します。

  • true: .tsx ファイルとして生成(TypeScript + JSX)
  • false: .jsx ファイルとして生成(JavaScript + JSX)

TypeScript プロジェクトでは true に設定しましょう。

具体例

実践的な設定例:企業プロジェクト向け

ここでは、実際の企業プロジェクトを想定した完全な components.json の設定例をご紹介します。

プロジェクト要件

以下の要件を持つプロジェクトを想定します。

  • Next.js 14 App Router 使用
  • TypeScript + 厳格な型チェック
  • カスタムデザインシステム(ブランドカラー適用)
  • モノレポ構成(Turborepo)
  • tw- プレフィックスで Tailwind クラスを管理

完全な設定ファイル

json{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "app/globals.css",
    "baseColor": "zinc",
    "cssVariables": true,
    "prefix": "tw-"
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  }
}

この設定により、以下のようなコンポーネントが生成されます。

生成されるコンポーネントの例

Button コンポーネントを追加すると、以下のような構造になります。

bashnpx shadcn-ui@latest add button

生成されるファイルのパス:

plaintextcomponents/
└── ui/
    └── button.tsx

ファイルの内容を確認していきましょう。

typescript'use client';

import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import {
  cva,
  type VariantProps,
} from 'class-variance-authority';

import { cn } from '@/lib/utils';

インポート部分では、設定したエイリアスが正しく使用されています。また、RSC が有効なため "use client" ディレクティブが自動追加されていますね。

次に、バリアント定義を見ていきます。

typescriptconst buttonVariants = cva(
  'tw-inline-flex tw-items-center tw-justify-center tw-whitespace-nowrap tw-rounded-md tw-text-sm tw-font-medium tw-ring-offset-background tw-transition-colors focus-visible:tw-outline-none focus-visible:tw-ring-2 focus-visible:tw-ring-ring focus-visible:tw-ring-offset-2 disabled:tw-pointer-events-none disabled:tw-opacity-50',
  {
    variants: {
      variant: {
        default:
          'tw-bg-primary tw-text-primary-foreground hover:tw-bg-primary/90',
        destructive:
          'tw-bg-destructive tw-text-destructive-foreground hover:tw-bg-destructive/90',
        outline:
          'tw-border tw-border-input tw-bg-background hover:tw-bg-accent hover:tw-text-accent-foreground',
        secondary:
          'tw-bg-secondary tw-text-secondary-foreground hover:tw-bg-secondary/80',
        ghost:
          'hover:tw-bg-accent hover:tw-text-accent-foreground',
        link: 'tw-text-primary tw-underline-offset-4 hover:tw-underline',
      },
      size: {
        default: 'tw-h-10 tw-px-4 tw-py-2',
        sm: 'tw-h-9 tw-rounded-md tw-px-3',
        lg: 'tw-h-11 tw-rounded-md tw-px-8',
        icon: 'tw-h-10 tw-w-10',
      },
    },
    defaultVariants: {
      variant: 'default',
      size: 'default',
    },
  }
);

prefix 設定により、すべての Tailwind クラスに tw- プレフィックスが付与されています。これにより、他の CSS フレームワークとの競合を避けられるでしょう。

最後に、コンポーネント本体を見ていきます。

typescriptexport interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
}

const Button = React.forwardRef<
  HTMLButtonElement,
  ButtonProps
>(
  (
    { className, variant, size, asChild = false, ...props },
    ref
  ) => {
    const Comp = asChild ? Slot : 'button';
    return (
      <Comp
        className={cn(
          buttonVariants({ variant, size, className })
        )}
        ref={ref}
        {...props}
      />
    );
  }
);
Button.displayName = 'Button';

export { Button, buttonVariants };

TypeScript の型定義が完全に含まれ、型安全性が確保されています。

add コマンドの実践的な使い方

components.json を適切に設定した後は、add コマンドでコンポーネントを追加していきます。

単一コンポーネントの追加

bashnpx shadcn-ui@latest add button

このコマンドは、以下の処理を自動的に実行します。

  1. components.json の設定を読み込み
  2. レジストリから Button コンポーネントを取得
  3. 設定に基づいてテンプレートを適用
  4. 指定されたパスにファイルを生成
  5. 必要な依存関係を package.json に追加

複数コンポーネントの同時追加

フォーム関連のコンポーネントをまとめて追加する例です。

bashnpx shadcn-ui@latest add button input label form

この方法により、関連するコンポーネントを一度に追加でき、開発効率が向上します。

対話式での追加

コンポーネント名を指定せずに実行すると、対話式で選択できます。

bashnpx shadcn-ui@latest add

プロンプトが表示され、利用可能なコンポーネント一覧から選択できるため、初めて使うコンポーネントを探す際に便利でしょう。

update コマンドの活用パターン

update コマンドは、既存のコンポーネントを最新版に更新する際に使用します。

特定コンポーネントの更新

bashnpx shadcn-ui@latest update button

このコマンドを実行すると、以下の動作が行われます。

mermaidsequenceDiagram
    participant Dev as 開発者
    participant CLI as shadcn/ui CLI
    participant Local as ローカルファイル
    participant Registry as レジストリ

    Dev->>CLI: update button
    CLI->>Local: 現在のバージョン確認
    CLI->>Registry: 最新バージョン取得
    CLI->>CLI: 差分比較
    CLI->>Dev: 変更内容表示
    Dev->>CLI: 更新確認
    CLI->>Local: ファイル更新
    CLI->>Dev: 更新完了通知

図で理解できる要点:

  • 現在のファイルと最新版を比較
  • 変更内容を確認してから更新可能
  • カスタマイズ箇所の上書きに注意が必要

すべてのコンポーネントの更新

プロジェクト内のすべてのコンポーネントを更新する場合は以下のコマンドを使用します。

bashnpx shadcn-ui@latest update

コンポーネント名を省略すると、インストール済みのすべてのコンポーネントが更新対象となります。

更新時のカスタマイズ保持戦略

update コマンドはファイルを上書きするため、カスタマイズを保持する工夫が必要です。

方法 1:拡張コンポーネントパターン

typescript// components/ui/button.tsx (shadcn/ui のオリジナル)
export { Button, buttonVariants };
typescript// components/custom/button.tsx (カスタマイズ版)
import {
  Button as BaseButton,
  buttonVariants,
} from '@/components/ui/button';
import { cn } from '@/lib/utils';

export function CustomButton({ className, ...props }) {
  return (
    <BaseButton
      className={cn('custom-additional-styles', className)}
      {...props}
    />
  );
}

この方法では、オリジナルのコンポーネントはそのまま保持し、カスタマイズは別ファイルで行います。update 時にカスタマイズが失われる心配がありません。

方法 2:バリアント拡張パターン

typescriptimport { buttonVariants as baseButtonVariants } from '@/components/ui/button';
import { cva } from 'class-variance-authority';

export const customButtonVariants = cva(
  baseButtonVariants(),
  {
    variants: {
      variant: {
        // 既存のバリアントを継承
        ...baseButtonVariants().variants?.variant,
        // カスタムバリアントを追加
        brand:
          'tw-bg-brand tw-text-white hover:tw-bg-brand/90',
        outline:
          'tw-border-2 tw-border-brand tw-text-brand',
      },
    },
  }
);

この方法により、元のバリアントを保持しつつ、プロジェクト固有のバリアントを追加できます。

チーム開発での運用フロー

components.json を活用したチーム開発の実践的なフローをご紹介します。

初期セットアップ

プロジェクト開始時に、リードエンジニアが components.json を設定します。

bash# プロジェクト初期化
npx shadcn-ui@latest init

対話式プロンプトで基本設定を行った後、components.json をプロジェクトの要件に合わせて調整します。

この設定ファイルを Git にコミットすることで、チーム全体で同じ設定を共有できるでしょう。

bashgit add components.json
git commit -m "chore: shadcn/ui 設定ファイルを追加"
git push origin main

チームメンバーの環境構築

新しいメンバーがプロジェクトに参加する際の手順です。

bash# リポジトリのクローン
git clone <repository-url>
cd <project-directory>

# 依存関係のインストール
yarn install

# components.json は既に存在するため、init は不要
# 必要なコンポーネントの追加
npx shadcn-ui@latest add button card dialog

components.json がリポジトリに含まれているため、全員が同じ設定でコンポーネントを追加できます。

コンポーネント追加のワークフロー

新しいコンポーネントが必要になった際のフローを見ていきましょう。

bash# 1. 新しいブランチを作成
git checkout -b feature/add-data-table

# 2. 必要なコンポーネントを追加
npx shadcn-ui@latest add table

# 3. 生成されたファイルを確認
git status

# 4. コミット
git add components/ui/table.tsx
git commit -m "feat: Table コンポーネントを追加"

# 5. プッシュしてプルリクエスト作成
git push origin feature/add-data-table

このフローにより、コンポーネント追加の履歴が Git に残り、レビューも可能になります。

package.json の管理

shadcn/ui のコンポーネントは、必要な依存関係を自動的に package.json に追加します。

json{
  "dependencies": {
    "@radix-ui/react-dialog": "^1.0.5",
    "@radix-ui/react-dropdown-menu": "^2.0.6",
    "@radix-ui/react-slot": "^1.0.2",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.0.0",
    "tailwind-merge": "^2.0.0"
  }
}

これらの依存関係は、yarn install によってチーム全体で同期されます。

高度なカスタマイズテクニック

さらに高度なカスタマイズ例をご紹介します。

カスタムカラーパレットの適用

プロジェクト固有のブランドカラーを使用する場合、globals.css をカスタマイズします。

css@layer base {
  :root {
    /* ブランドカラーの定義 */
    --brand-primary: 210 100% 50%;
    --brand-secondary: 280 70% 60%;
    --brand-accent: 45 100% 55%;

    /* shadcn/ui のカラー変数をブランドカラーで上書き */
    --primary: var(--brand-primary);
    --secondary: var(--brand-secondary);
    --accent: var(--brand-accent);

    /* その他の設定 */
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --card: 0 0% 100%;
    --card-foreground: 222.2 84% 4.9%;
    --border: 214.3 31.8% 91.4%;
    --input: 214.3 31.8% 91.4%;
    --ring: var(--brand-primary);
    --radius: 0.5rem;
  }
}

この設定により、すべてのコンポーネントがブランドカラーを使用するようになります。

デザイントークンの統合

デザインシステムを持つ組織では、デザイントークンとの統合も重要です。

typescript// lib/design-tokens.ts
export const designTokens = {
  colors: {
    primary: {
      50: 'hsl(210, 100%, 95%)',
      100: 'hsl(210, 100%, 90%)',
      // ... 他のシェード
      500: 'hsl(210, 100%, 50%)',
      900: 'hsl(210, 100%, 20%)',
    },
  },
  spacing: {
    xs: '0.25rem',
    sm: '0.5rem',
    md: '1rem',
    lg: '1.5rem',
    xl: '2rem',
  },
  typography: {
    fontFamily: {
      sans: ['Inter', 'sans-serif'],
      mono: ['Fira Code', 'monospace'],
    },
  },
} as const;

これらのトークンを Tailwind の設定に統合します。

typescript// tailwind.config.ts
import type { Config } from 'tailwindcss';
import { designTokens } from './lib/design-tokens';

const config = {
  content: [
    './pages/**/*.{ts,tsx}',
    './components/**/*.{ts,tsx}',
    './app/**/*.{ts,tsx}',
  ],
  prefix: 'tw-',
  theme: {
    extend: {
      colors: {
        primary: designTokens.colors.primary,
      },
      spacing: designTokens.spacing,
      fontFamily: designTokens.typography.fontFamily,
    },
  },
} satisfies Config;

export default config;

この統合により、デザインとコードの一貫性が保たれるでしょう。

まとめ

shadcn/ui CLI の add/update コマンドとカスタムテンプレート運用について、実践的な活用方法をご紹介しました。

components.json の適切な設定により、以下のメリットが得られます。

  • プロジェクト固有のディレクトリ構成への対応
  • ブランドカラーやデザインシステムの自動適用
  • チーム全体での一貫したコンポーネント管理
  • 更新時のカスタマイズ保持戦略

特に重要なポイントをまとめます。

#項目重要度推奨アプローチ
1エイリアス設定★★★プロジェクト構成に合わせて必ず設定
2Tailwind 統合★★★baseColor と prefix を要件に応じて調整
3RSC 設定★★☆Next.js App Router では true に設定
4スタイル選択★★☆デザイントーンに合わせて選択
5カスタマイズ保持★★★拡張パターンを使用して上書きを回避

components.json はプロジェクトの成長に合わせて進化させるものです。初期設定で完璧を目指すのではなく、チームのニーズに応じて段階的に最適化していくことをお勧めします。

カスタムテンプレート運用を導入することで、開発効率が向上し、コードベースの品質も安定するでしょう。ぜひ、あなたのプロジェクトでも試してみてくださいね。

関連リンク