T-CREATOR

shadcn/ui を Monorepo(Turborepo/pnpm)に導入するベストプラクティス

shadcn/ui を Monorepo(Turborepo/pnpm)に導入するベストプラクティス

現代のフロントエンド開発において、複数のアプリケーションを効率的に管理できる Monorepo 構成が注目されています。特に UI コンポーネントライブラリとして人気の高い shadcn/ui を、Turborepo と pnpm の Monorepo 環境に導入する際には、いくつかのポイントを押さえることで、開発効率を大幅に向上させることができますね。

本記事では、shadcn/ui を Turborepo + pnpm 環境で最適に活用するための導入手順から運用のベストプラクティスまでを詳しく解説いたします。

Monorepo での UI コンポーネント管理の課題

Monorepo 環境で UI コンポーネントを管理する際、開発者が直面する主な課題を整理してみましょう。

コンポーネントの重複と一貫性の問題

複数のアプリケーションで似たようなコンポーネントが重複して作成されることがあります。これにより、メンテナンスコストが増大し、デザインの一貫性も保ちにくくなってしまいますね。

以下の図は、Monorepo 環境での典型的なコンポーネント管理の課題を表しています。

mermaidflowchart TD
  monorepo["Monorepo"]
  app1["App 1"]
  app2["App 2"]
  app3["App 3"]

  components1["独自コンポーネント<br/>・Button<br/>・Modal<br/>・Form"]
  components2["独自コンポーネント<br/>・Button<br/>・Dialog<br/>・Input"]
  components3["独自コンポーネント<br/>・Button<br/>・Popup<br/>・TextField"]

  monorepo --> app1
  monorepo --> app2
  monorepo --> app3

  app1 --> components1
  app2 --> components2
  app3 --> components3

  style components1 fill:#ffcccc
  style components2 fill:#ffcccc
  style components3 fill:#ffcccc

この図から分かるように、各アプリケーションで似たような機能のコンポーネントが重複して開発されているのが課題となります。

依存関係の複雑化

Monorepo 内の各パッケージ間で依存関係が複雑になりがちです。特に UI コンポーネントライブラリを導入する際は、適切なパッケージ構成を設計しないと、ビルド時間の増大や循環依存の問題が発生する可能性があります。

スタイリングの統一

各アプリケーションでスタイリングの手法やテーマが異なると、統一感のない UI になってしまいます。Tailwind CSS の設定やカスタムテーマの管理も課題の一つですね。

shadcn/ui とは

shadcn/ui は、React 開発者の間で急速に人気を集めているコンポーネントライブラリです。従来のライブラリとは異なる特徴的なアプローチを採用しています。

コピー&ペースト方式の革新性

shadcn/ui の最大の特徴は、コンポーネントを npm パッケージとして配布するのではなく、コードをプロジェクトに直接コピーする方式を採用していることです。

typescript// 従来のライブラリの使用方法
import { Button } from '@mui/material';
import { Input } from 'antd';

// shadcn/uiの使用方法
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';

この方式により、以下のメリットが得られます。

  • 完全なカスタマイズ性: コンポーネントのソースコードが手元にあるため、必要に応じて自由に変更できます
  • 依存関係の軽量化: 必要なコンポーネントのみを取り込むため、バンドルサイズを最適化できます
  • 学習効果: 実装を直接確認できるため、React や Tailwind CSS の学習にも役立ちます

Tailwind CSS との緊密な統合

shadcn/ui は、Tailwind CSS をベースとしたスタイリングシステムを採用しています。これにより、デザインシステムの一貫性を保ちながら、柔軟なカスタマイズが可能になります。

typescript// shadcn/uiのButtonコンポーネント例
export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
}

const buttonVariants = cva(
  'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors',
  {
    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',
      },
      size: {
        default: 'h-10 px-4 py-2',
        sm: 'h-9 rounded-md px-3',
        lg: 'h-11 rounded-md px-8',
      },
    },
  }
);

このコードは、class-variance-authority(cva)を使用してバリアントベースのスタイリングを実現しています。

Turborepo + pnpm 環境での shadcn/ui 導入手順

ここからは、具体的な導入手順を段階的に説明していきます。効率的な Monorepo 環境を構築するための重要なステップですね。

Monorepo 環境の準備

まず、Turborepo と pnpm を使用した Monorepo 環境をセットアップしましょう。

1. プロジェクトの初期化

bash# プロジェクト作成
npx create-turbo@latest my-monorepo --package-manager pnpm
cd my-monorepo

2. 基本的なディレクトリ構造の確認

perlmy-monorepo/
├── apps/
│   ├── web/                 # Next.js アプリケーション
│   └── docs/                # ドキュメントサイト
├── packages/
│   ├── ui/                  # 共有UIコンポーネント
│   ├── eslint-config/       # ESLint設定
│   └── typescript-config/   # TypeScript設定
├── package.json
├── pnpm-workspace.yaml
└── turbo.json

この構造により、共有可能なコンポーネントとアプリケーション固有のコードを明確に分離できます。

3. pnpm-workspace.yaml の設定

yamlpackages:
  - 'apps/*'
  - 'packages/*'

パッケージ構成の設計

shadcn/ui を効果的に活用するためのパッケージ構成を設計しましょう。

共有 UI パッケージの作成

packages​/​ui ディレクトリに共有 UI コンポーネントライブラリを作成します。

json{
  "name": "@repo/ui",
  "version": "0.0.0",
  "main": "./index.tsx",
  "types": "./index.tsx",
  "license": "MIT",
  "scripts": {
    "lint": "eslint . --max-warnings 0",
    "generate:component": "npx shadcn-ui@latest add"
  },
  "devDependencies": {
    "@repo/eslint-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@types/react": "^18.2.61",
    "@types/react-dom": "^18.2.19",
    "eslint": "^8.57.0",
    "react": "^18.2.0",
    "typescript": "^5.3.3"
  },
  "dependencies": {
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.0",
    "tailwind-merge": "^2.2.1"
  },
  "peerDependencies": {
    "react": "^18.2.0"
  }
}

アプリケーション側の package.json 設定

各アプリケーションから共有 UI パッケージを参照できるよう設定します。

json{
  "name": "web",
  "dependencies": {
    "@repo/ui": "workspace:*",
    "next": "^14.1.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}

shadcn/ui のインストールと設定

次に、shadcn/ui を実際にプロジェクトに導入していきます。

1. 基本依存関係のインストール

共有 UI パッケージに必要な依存関係をインストールします。

bash# packages/ui ディレクトリで実行
cd packages/ui
pnpm add class-variance-authority clsx tailwind-merge
pnpm add -D tailwindcss autoprefixer postcss

2. Tailwind CSS の設定

packages​/​ui​/​tailwind.config.js を作成します。

javascript/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
  theme: {
    extend: {
      colors: {
        border: 'hsl(var(--border))',
        input: 'hsl(var(--input))',
        ring: 'hsl(var(--ring))',
        background: 'hsl(var(--background))',
        foreground: 'hsl(var(--foreground))',
        primary: {
          DEFAULT: 'hsl(var(--primary))',
          foreground: 'hsl(var(--primary-foreground))',
        },
        secondary: {
          DEFAULT: 'hsl(var(--secondary))',
          foreground: 'hsl(var(--secondary-foreground))',
        },
      },
      borderRadius: {
        lg: 'var(--radius)',
        md: 'calc(var(--radius) - 2px)',
        sm: 'calc(var(--radius) - 4px)',
      },
    },
  },
  plugins: [],
};

3. CSS 変数の定義

packages​/​ui​/​src​/​globals.css を作成し、テーマの基本スタイルを定義します。

css@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 84% 4.9%;
    --card: 0 0% 100%;
    --card-foreground: 222.2 84% 4.9%;
    --popover: 0 0% 100%;
    --popover-foreground: 222.2 84% 4.9%;
    --primary: 222.2 47.4% 11.2%;
    --primary-foreground: 210 40% 98%;
    --secondary: 210 40% 96%;
    --secondary-foreground: 222.2 47.4% 11.2%;
    --muted: 210 40% 96%;
    --muted-foreground: 215.4 16.3% 46.9%;
    --accent: 210 40% 96%;
    --accent-foreground: 222.2 47.4% 11.2%;
    --destructive: 0 84.2% 60.2%;
    --destructive-foreground: 210 40% 98%;
    --border: 214.3 31.8% 91.4%;
    --input: 214.3 31.8% 91.4%;
    --ring: 222.2 84% 4.9%;
    --radius: 0.5rem;
  }

  .dark {
    --background: 222.2 84% 4.9%;
    --foreground: 210 40% 98%;
    --card: 222.2 84% 4.9%;
    --card-foreground: 210 40% 98%;
    --popover: 222.2 84% 4.9%;
    --popover-foreground: 210 40% 98%;
    --primary: 210 40% 98%;
    --primary-foreground: 222.2 47.4% 11.2%;
    --secondary: 217.2 32.6% 17.5%;
    --secondary-foreground: 210 40% 98%;
    --muted: 217.2 32.6% 17.5%;
    --muted-foreground: 215 20.2% 65.1%;
    --accent: 217.2 32.6% 17.5%;
    --accent-foreground: 210 40% 98%;
    --destructive: 0 62.8% 30.6%;
    --destructive-foreground: 210 40% 98%;
    --border: 217.2 32.6% 17.5%;
    --input: 217.2 32.6% 17.5%;
    --ring: 212.7 26.8% 83.9%;
  }
}

4. ユーティリティ関数の作成

packages​/​ui​/​src​/​lib​/​utils.ts を作成します。

typescriptimport { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

この関数は、Tailwind CSS のクラス名を効率的にマージするために使用します。

コンポーネントの共有設定

shadcn/ui のコンポーネントを共有パッケージに追加し、各アプリケーションから利用できるように設定します。

1. shadcn/ui CLI の設定

packages​/​ui ディレクトリに components.json を作成します。

json{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "default",
  "rsc": false,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.js",
    "css": "src/globals.css",
    "baseColor": "slate",
    "cssVariables": true
  },
  "aliases": {
    "components": "./src/components",
    "utils": "./src/lib/utils"
  }
}

2. 基本コンポーネントの追加

shadcn/ui CLI を使用して Button コンポーネントを追加してみましょう。

bash# packages/ui ディレクトリで実行
npx shadcn-ui@latest add button

これにより、以下のファイルが自動生成されます。

typescript// packages/ui/src/components/ui/button.tsx
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';

const buttonVariants = cva(
  'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
  {
    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',
      },
      size: {
        default: 'h-10 px-4 py-2',
        sm: 'h-9 rounded-md px-3',
        lg: 'h-11 rounded-md px-8',
        icon: 'h-10 w-10',
      },
    },
    defaultVariants: {
      variant: 'default',
      size: 'default',
    },
  }
);

export 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 };

3. エクスポート設定の作成

packages​/​ui​/​index.tsx でコンポーネントをエクスポートします。

typescript// コンポーネントのエクスポート
export { Button, type ButtonProps } from './src/components/ui/button'

// スタイルのエクスポート
export './src/globals.css'

// ユーティリティのエクスポート
export { cn } from './src/lib/utils'

以下の図は、Monorepo 環境での shadcn/ui コンポーネント共有の全体像を示しています。

mermaidflowchart TD
  ui_package["@repo/ui パッケージ"]
  shadcn_cli["shadcn/ui CLI"]
  button_comp["Button コンポーネント"]
  input_comp["Input コンポーネント"]
  app1["Web App"]
  app2["Admin App"]

  shadcn_cli -->|コンポーネント生成| ui_package
  ui_package --> button_comp
  ui_package --> input_comp

  ui_package -->|workspace:*| app1
  ui_package -->|workspace:*| app2

  style ui_package fill:#e1f5fe
  style app1 fill:#f3e5f5
  style app2 fill:#f3e5f5

この構成により、1 つの共有パッケージから複数のアプリケーションにコンポーネントを配布できます。

最適化とベストプラクティス

shadcn/ui を Monorepo 環境で効果的に活用するためのベストプラクティスを見ていきましょう。

ビルド最適化

Turborepo の機能を活用して、効率的なビルドパイプラインを構築します。

turbo.json の最適化設定

プロジェクトルートの turbo.json を以下のように設定します。

json{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", "dist/**"]
    },
    "lint": {
      "dependsOn": ["^lint"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "type-check": {
      "dependsOn": ["^build"],
      "outputs": []
    }
  }
}

段階的ビルドの実装

各パッケージに適切なビルドスクリプトを設定します。

json// packages/ui/package.json
{
  "scripts": {
    "build": "tsc && cp -r src/components dist/",
    "dev": "tsc --watch",
    "type-check": "tsc --noEmit",
    "lint": "eslint src --ext .ts,.tsx"
  }
}

キャッシュ戦略の活用

Turborepo のキャッシュ機能を最大限活用することで、開発効率を向上させることができます。

bash# 変更されたパッケージのみビルド
pnpm turbo build --filter=...@repo/ui

# 並列実行でビルド時間短縮
pnpm turbo build --parallel

型安全性の確保

TypeScript を活用して、コンポーネント間の型安全性を確保しましょう。

共通型定義の作成

packages​/​ui​/​src​/​types​/​index.ts で共通の型定義を管理します。

typescript// 基本的なコンポーネントプロパティ
export interface BaseComponentProps {
  className?: string;
  children?: React.ReactNode;
}

// テーマに関する型定義
export type Theme = 'light' | 'dark';

// バリアント型定義
export type ButtonVariant =
  | 'default'
  | 'destructive'
  | 'outline'
  | 'secondary'
  | 'ghost'
  | 'link';
export type ButtonSize = 'default' | 'sm' | 'lg' | 'icon';

// コンポーネント拡張のための型
export interface ComponentVariants {
  variant?: ButtonVariant;
  size?: ButtonSize;
}

厳密な型チェックの設定

packages​/​ui​/​tsconfig.json で厳密な型チェックを有効にします。

json{
  "extends": "@repo/typescript-config/react-library.json",
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

開発体験の向上

開発者が効率的に作業できる環境を整備します。

Storybook の統合

コンポーネントのドキュメント化とテストのために Storybook を導入しましょう。

bash# packages/ui ディレクトリで実行
npx storybook@latest init

packages​/​ui​/​.storybook​/​main.ts を設定します。

typescriptimport type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@storybook/addon-docs',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
  docs: {
    autodocs: 'tag',
  },
  typescript: {
    check: false,
    reactDocgen: 'react-docgen-typescript',
  },
};

export default config;

コンポーネントのストーリー作成

packages​/​ui​/​src​/​components​/​ui​/​button.stories.tsx を作成します。

typescriptimport type { Meta, StoryObj } from '@storybook/react';
import { Button } from './button';

const meta: Meta<typeof Button> = {
  title: 'UI/Button',
  component: Button,
  parameters: {
    layout: 'centered',
  },
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: { type: 'select' },
      options: [
        'default',
        'destructive',
        'outline',
        'secondary',
        'ghost',
        'link',
      ],
    },
    size: {
      control: { type: 'select' },
      options: ['default', 'sm', 'lg', 'icon'],
    },
  },
};

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
  args: {
    children: 'Button',
  },
};

export const Secondary: Story = {
  args: {
    variant: 'secondary',
    children: 'Secondary Button',
  },
};

export const Destructive: Story = {
  args: {
    variant: 'destructive',
    children: 'Delete',
  },
};

自動コンポーネント生成スクリプト

新しいコンポーネントを効率的に作成するためのスクリプトを用意します。

bash#!/bin/bash
# scripts/create-component.sh

COMPONENT_NAME=$1

if [ -z "$COMPONENT_NAME" ]; then
  echo "Usage: ./create-component.sh <ComponentName>"
  exit 1
fi

# shadcn/ui コンポーネントを追加
cd packages/ui
npx shadcn-ui@latest add $COMPONENT_NAME

# Storybook ストーリーファイルを自動生成
cat > "src/components/ui/${COMPONENT_NAME}.stories.tsx" << EOF
import type { Meta, StoryObj } from '@storybook/react'
import { ${COMPONENT_NAME} } from './${COMPONENT_NAME}'

const meta: Meta<typeof ${COMPONENT_NAME}> = {
  title: 'UI/${COMPONENT_NAME}',
  component: ${COMPONENT_NAME},
  parameters: {
    layout: 'centered'
  },
  tags: ['autodocs']
}

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {
  args: {}
}
EOF

echo "${COMPONENT_NAME} component and story created successfully!"

このスクリプトを実行することで、shadcn/ui コンポーネントと Storybook ストーリーを同時に生成できます。

トラブルシューティング

Monorepo 環境で shadcn/ui を運用する際によく発生する問題と解決策をまとめました。

依存関係の重複問題

問題: React や Tailwind CSS の重複インストール

複数のパッケージで同じ依存関係がインストールされ、バンドルサイズが増大する問題です。

解決策: peerDependencies の活用

共有 UI パッケージでは、React や Tailwind CSS を peerDependencies として定義します。

json// packages/ui/package.json
{
  "peerDependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}

プロジェクトルートの .npmrc で重複を防ぎます。

ini# .npmrc
strict-peer-dependencies=false
auto-install-peers=true

TypeScript パス解決の問題

問題: モジュール解決エラー

@​/​components​/​ui​/​button のようなパスエイリアスが解決されない問題です。

解決策: TypeScript パスマッピングの統一

各パッケージの tsconfig.json でパスマッピングを統一します。

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

ビルド時の CSS 競合

問題: Tailwind CSS クラスの競合

複数のパッケージで異なる Tailwind 設定が適用される問題です。

解決策: 統一された Tailwind 設定の共有

packages​/​tailwind-config パッケージを作成し、設定を統一します。

javascript// packages/tailwind-config/index.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [],
  theme: {
    extend: {
      colors: {
        border: 'hsl(var(--border))',
        input: 'hsl(var(--input))',
        ring: 'hsl(var(--ring))',
        background: 'hsl(var(--background))',
        foreground: 'hsl(var(--foreground))',
        primary: {
          DEFAULT: 'hsl(var(--primary))',
          foreground: 'hsl(var(--primary-foreground))',
        },
      },
    },
  },
  plugins: [],
};

各パッケージで共通設定を継承します。

javascript// packages/ui/tailwind.config.js
const baseConfig = require('@repo/tailwind-config');

/** @type {import('tailwindcss').Config} */
module.exports = {
  ...baseConfig,
  content: ['./src/**/*.{js,ts,jsx,tsx}'],
};

循環依存の問題

問題: パッケージ間の循環依存

パッケージ間で相互に依存関係が発生し、ビルドエラーが生じる問題です。

解決策: 依存関係グラフの可視化と整理

Turborepo の機能を使用して依存関係を可視化し、問題を特定します。

bash# 依存関係グラフの生成
pnpm turbo build --graph

# 特定のパッケージの依存関係を確認
pnpm turbo build --filter=@repo/ui --graph

問題が見つかった場合は、共通のロジックを別パッケージに分離します。

bashpackages/
├── ui/              # UIコンポーネント
├── utils/           # 共通ユーティリティ(新規作成)
└── types/           # 共通型定義(新規作成)

Hot Reload の不具合

問題: 開発時にコンポーネントの変更が反映されない

Monorepo 環境で、共有パッケージの変更がアプリケーション側に即座に反映されない問題です。

解決策: Watch モードの設定

各パッケージで watch モードを設定します。

json// packages/ui/package.json
{
  "scripts": {
    "dev": "tsc --watch --preserveWatchOutput",
    "build": "tsc"
  }
}

Turborepo で parallel 実行を設定します。

bash# すべてのパッケージでdev モードを同時実行
pnpm turbo dev --parallel

まとめ

shadcn/ui を Turborepo + pnpm 環境に導入することで、モダンで効率的な Monorepo 開発環境を構築できました。本記事で紹介した手法により、以下のメリットを享受できます。

主な成果

  • コンポーネントの一元管理: 複数のアプリケーション間で UI コンポーネントを効率的に共有
  • 開発効率の向上: shadcn/ui のコピー&ペースト方式により、柔軟なカスタマイズが可能
  • ビルド最適化: Turborepo のキャッシュ機能と pnpm の効率的なパッケージ管理
  • 型安全性の確保: TypeScript による厳密な型チェックでランタイムエラーを防止

重要なポイントの振り返り

  1. パッケージ構成の設計: 共有 UI パッケージを中心とした明確な責務分離
  2. 依存関係の管理: peerDependencies の活用による重複排除
  3. 開発体験の最適化: Storybook 統合と Hot Reload の実現
  4. トラブルシューティング: よく発生する問題への事前対策

これらの実装により、スケーラブルで保守性の高いフロントエンド開発環境を構築することができますね。

shadcn/ui と Monorepo の組み合わせは、チーム開発において特に威力を発揮します。今後も新しいコンポーネントの追加や既存コンポーネントのカスタマイズを繰り返しながら、プロジェクトに最適なデザインシステムを育てていってください。

関連リンク