Storybook を Monorepo に導入:Yarn Workspaces/Turborepo の最短レシピ

Monorepo で複数のプロジェクトを管理していると、コンポーネントの可視化や開発効率の向上のために Storybook を導入したくなりますよね。しかし、Yarn Workspaces や Turborepo といった Monorepo ツールと Storybook を組み合わせる際、設定の複雑さや依存関係の解決に悩まされることも少なくありません。
本記事では、Yarn Workspaces と Turborepo を使った Monorepo 環境に Storybook を最短で導入する手順を、初心者の方でもわかりやすく解説します。実際に動作するサンプルコードとともに、つまずきやすいポイントも丁寧にご紹介しますので、ぜひ最後までお読みください。
背景
Monorepo とは
Monorepo(モノレポ)は、複数のプロジェクトやパッケージを 1 つのリポジトリで管理する開発手法です。
従来の Multi-repo では、各プロジェクトが独立したリポジトリを持つため、共通コードの管理やバージョン管理が煩雑になりがちでした。Monorepo を採用することで、コードの再利用性が向上し、依存関係の管理が容易になります。
以下の図は、Monorepo の基本構造を示しています。
mermaidflowchart TB
  root["Monorepo ルート"]
  root --> packages["packages/"]
  packages --> ui["ui パッケージ"]
  packages --> utils["utils パッケージ"]
  packages --> app["app パッケージ"]
  ui --> ui_components["React コンポーネント"]
  utils --> util_funcs["共通関数"]
  app --> app_code["アプリケーション<br/>コード"]
  app -.依存.-> ui
  app -.依存.-> utils
この構成により、app パッケージは ui や utils を簡単に参照でき、コードの重複を避けられます。
Yarn Workspaces と Turborepo の役割
Yarn Workspaces は、Yarn が提供する Monorepo 管理機能です。複数のパッケージを 1 つの node_modules で管理し、依存関係の解決を効率化します。
Turborepo は、Monorepo 向けのビルドシステムで、タスクのキャッシュや並列実行により、ビルド時間を大幅に短縮できます。
以下の図は、両者の役割分担を示しています。
mermaidflowchart LR
  yarn["Yarn Workspaces"]
  turbo["Turborepo"]
  deps["依存関係管理"]
  build["ビルド最適化"]
  yarn --> deps
  turbo --> build
  deps --> node["node_modules<br/>共通化"]
  build --> cache["キャッシュ/<br/>並列実行"]
Yarn Workspaces がパッケージの依存を一元管理し、Turborepo がビルドプロセスを最適化することで、開発体験が大きく向上します。
Storybook の役割
Storybook は、UI コンポーネントを独立した環境で開発・テストするためのツールです。
コンポーネントカタログとして機能し、デザイナーや他の開発者との共有もスムーズに行えます。Monorepo 環境では、複数のパッケージにまたがるコンポーネントを一元管理できるため、Storybook の導入は特に有効です。
課題
Monorepo 環境での Storybook 導入の難しさ
Monorepo に Storybook を導入する際、以下のような課題に直面することがあります。
- 依存関係の解決: Workspace 間のパッケージ参照が正しく認識されない
- 設定ファイルの配置: Storybook の設定をどのパッケージに置くべきか判断が難しい
- ビルドキャッシュの管理: Turborepo のキャッシュと Storybook のビルドが干渉する
- パス解決の問題: TypeScript や Webpack のパス解決が複雑になる
これらの課題を解決しないと、Storybook が正しく起動しなかったり、コンポーネントが表示されなかったりします。
以下の図は、典型的な問題の発生フローを示しています。
mermaidflowchart TD
  start["Storybook 起動"]
  start --> check_deps["依存関係チェック"]
  check_deps -->|失敗| err1["Error: Module<br/>not found"]
  check_deps -->|成功| check_path["パス解決"]
  check_path -->|失敗| err2["Error: Cannot<br/>resolve path"]
  check_path -->|成功| check_build["ビルド実行"]
  check_build -->|失敗| err3["Error: Build<br/>failed"]
  check_build -->|成功| success["Storybook<br/>正常起動"]
これらのエラーを回避するために、適切な設定が必要となります。
解決策
基本方針
Monorepo に Storybook を導入する際の基本方針は以下の通りです。
- 専用パッケージとして Storybook を配置: packages/storybookなど専用のパッケージを作成する
- Workspace の依存関係を明示: 他のパッケージを参照する場合は package.jsonに明記する
- Turborepo のタスクに統合: turbo.jsonに Storybook のビルドタスクを追加する
- パス解決を統一: TypeScript の tsconfig.jsonや Webpack の設定でパスエイリアスを統一する
この方針に従うことで、Monorepo 環境でもスムーズに Storybook を動作させられます。
ディレクトリ構成
以下のようなディレクトリ構成を想定します。
csharpmonorepo-root/
├─ package.json          # ルートの package.json
├─ turbo.json            # Turborepo の設定
├─ yarn.lock
└─ packages/
   ├─ ui/                # UI コンポーネントパッケージ
   │  ├─ package.json
   │  ├─ src/
   │  │  └─ Button.tsx
   │  └─ tsconfig.json
   ├─ utils/             # ユーティリティパッケージ
   │  ├─ package.json
   │  └─ src/
   │     └─ format.ts
   └─ storybook/         # Storybook 専用パッケージ
      ├─ package.json
      ├─ .storybook/
      │  ├─ main.ts
      │  └─ preview.ts
      └─ stories/
         └─ Button.stories.tsx
この構成により、Storybook を独立したパッケージとして管理でき、他のパッケージへの影響を最小限に抑えられます。
設計のポイント
以下の図は、Storybook パッケージと他パッケージの関係を示しています。
mermaidflowchart LR
  sb["storybook<br/>パッケージ"]
  ui["ui<br/>パッケージ"]
  utils["utils<br/>パッケージ"]
  sb -.参照.-> ui
  sb -.参照.-> utils
  ui -.参照.-> utils
Storybook パッケージは、UI コンポーネントやユーティリティを参照するだけで、逆方向の依存は発生しません。これにより、依存関係がシンプルになります。
具体例
ステップ 1: Monorepo の初期設定
まず、Yarn Workspaces を有効にした Monorepo を作成します。
ルートディレクトリに package.json を作成し、以下のように設定してください。
json{
  "name": "monorepo-example",
  "private": true,
  "workspaces": ["packages/*"],
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "storybook": "yarn workspace storybook storybook"
  },
  "devDependencies": {
    "turbo": "^1.10.0"
  }
}
ここでは、workspaces フィールドで packages/* 配下のすべてのディレクトリを Workspace として登録しています。
また、scripts には Turborepo を使ったビルドコマンドと、後ほど作成する Storybook の起動コマンドを定義しています。
ステップ 2: Turborepo の設定
次に、Turborepo の設定ファイル turbo.json をルートに作成します。
json{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"]
    },
    "dev": {
      "cache": false
    },
    "storybook": {
      "cache": false,
      "dependsOn": ["^build"]
    }
  }
}
この設定により、build タスクは依存パッケージのビルドが完了してから実行されます。
storybook タスクは、キャッシュを無効化し、依存パッケージのビルド完了後に実行されるように設定しています。これにより、常に最新のコンポーネントが Storybook に反映されます。
ステップ 3: UI パッケージの作成
UI コンポーネントを管理する packages/ui を作成します。
まず、packages/ui/package.json を作成してください。
json{
  "name": "ui",
  "version": "1.0.0",
  "main": "./src/index.ts",
  "scripts": {
    "build": "tsc"
  },
  "devDependencies": {
    "@types/react": "^18.2.0",
    "react": "^18.2.0",
    "typescript": "^5.0.0"
  },
  "peerDependencies": {
    "react": "^18.2.0"
  }
}
このパッケージでは、React コンポーネントを TypeScript で開発し、tsc でビルドします。
次に、packages/ui/tsconfig.json を作成します。
json{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "jsx": "react-jsx",
    "declaration": true,
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
これにより、TypeScript のコンパイル設定が完了します。
次に、サンプルのボタンコンポーネントを作成しましょう。packages/ui/src/Button.tsx を作成してください。
typescriptimport React from 'react';
// ボタンコンポーネントのプロパティ型定義
export interface ButtonProps {
  label: string; // ボタンに表示するテキスト
  onClick?: () => void; // クリック時のコールバック
  variant?: 'primary' | 'secondary'; // ボタンのスタイルバリエーション
}
ここでは、ボタンのプロパティを定義しています。variant プロパティで、ボタンのスタイルを切り替えられるようにしています。
次に、ボタンコンポーネント本体を実装します。
typescript// ボタンコンポーネントの実装
export const Button: React.FC<ButtonProps> = ({
  label,
  onClick,
  variant = 'primary',
}) => {
  // スタイルをバリエーションに応じて切り替え
  const style: React.CSSProperties = {
    padding: '10px 20px',
    border: 'none',
    borderRadius: '4px',
    cursor: 'pointer',
    backgroundColor:
      variant === 'primary' ? '#007bff' : '#6c757d',
    color: '#fff',
    fontSize: '14px',
  };
  return (
    <button style={style} onClick={onClick}>
      {label}
    </button>
  );
};
シンプルなボタンコンポーネントですが、variant によってスタイルが変わることがわかります。
最後に、packages/ui/src/index.ts でエクスポートします。
typescript// ui パッケージのエントリーポイント
export { Button } from './Button';
export type { ButtonProps } from './Button';
これで、UI パッケージの準備が整いました。
ステップ 4: Storybook パッケージの作成
次に、Storybook 専用のパッケージ packages/storybook を作成します。
まず、packages/storybook/package.json を作成してください。
json{
  "name": "storybook",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build"
  },
  "dependencies": {
    "ui": "workspace:*"
  },
  "devDependencies": {
    "@storybook/react": "^7.6.0",
    "@storybook/react-vite": "^7.6.0",
    "@storybook/addon-essentials": "^7.6.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "storybook": "^7.6.0",
    "vite": "^5.0.0"
  }
}
ここでは、dependencies に "ui": "workspace:*" を指定することで、Workspace 内の ui パッケージを参照しています。
また、Storybook 7 系と Vite を使った構成を採用しています。Vite はビルドが高速で、開発体験が向上します。
次に、Storybook の設定ファイル packages/storybook/.storybook/main.ts を作成します。
typescriptimport type { StorybookConfig } from '@storybook/react-vite';
// Storybook のメイン設定
const config: StorybookConfig = {
  // ストーリーファイルの場所を指定
  stories: ['../stories/**/*.stories.@(ts|tsx|js|jsx)'],
  // 使用するアドオンを指定
  addons: [
    '@storybook/addon-essentials', // 基本的なアドオン群
  ],
  // 使用するフレームワークを指定
  framework: {
    name: '@storybook/react-vite', // React + Vite を使用
    options: {},
  },
  // ドキュメント生成の設定
  docs: {
    autodocs: 'tag', // @docs タグがあるストーリーは自動でドキュメント生成
  },
};
export default config;
この設定により、stories ディレクトリ配下のストーリーファイルが自動で読み込まれます。
次に、プレビュー設定 packages/storybook/.storybook/preview.ts を作成します。
typescriptimport type { Preview } from '@storybook/react';
// Storybook プレビュー画面の設定
const preview: Preview = {
  parameters: {
    // アクション(イベントログ)の設定
    actions: { argTypesRegex: '^on[A-Z].*' },
    // コントロールパネルの設定
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
  },
};
export default preview;
この設定により、onClick などのイベントハンドラが自動的にアクションパネルに表示されます。
ステップ 5: ストーリーファイルの作成
次に、ボタンコンポーネント用のストーリーファイル packages/storybook/stories/Button.stories.tsx を作成します。
まず、インポート部分を記述します。
typescriptimport type { Meta, StoryObj } from '@storybook/react';
import { Button } from 'ui';
// Button コンポーネントのメタデータ設定
const meta: Meta<typeof Button> = {
  title: 'Components/Button', // Storybook 上での表示カテゴリ
  component: Button, // 対象のコンポーネント
  tags: ['autodocs'], // 自動ドキュメント生成を有効化
};
export default meta;
ここでは、ui パッケージから Button をインポートし、Storybook のメタデータを設定しています。
次に、具体的なストーリーを定義します。
typescript// Story の型定義
type Story = StoryObj<typeof Button>;
// Primary バリエーションのストーリー
export const Primary: Story = {
  args: {
    label: 'Primary Button',
    variant: 'primary',
  },
};
// Secondary バリエーションのストーリー
export const Secondary: Story = {
  args: {
    label: 'Secondary Button',
    variant: 'secondary',
  },
};
これにより、Storybook 上で Primary と Secondary の 2 つのバリエーションが表示されます。
さらに、クリックイベント付きのストーリーも作成しましょう。
typescript// クリックイベント付きのストーリー
export const WithClick: Story = {
  args: {
    label: 'Click Me',
    variant: 'primary',
    onClick: () => alert('Button clicked!'),
  },
};
このストーリーでは、ボタンをクリックするとアラートが表示されます。Storybook のアクションパネルでイベントの発火も確認できます。
ステップ 6: Storybook の起動
すべての設定が完了したら、Storybook を起動してみましょう。
ルートディレクトリで以下のコマンドを実行してください。
bashyarn install
これにより、すべての Workspace の依存関係がインストールされます。
次に、UI パッケージをビルドします。
bashyarn workspace ui build
このコマンドで、TypeScript のコンパイルが実行され、packages/ui/dist にビルド結果が出力されます。
最後に、Storybook を起動します。
bashyarn storybook
ブラウザで http://localhost:6006 にアクセスすると、Storybook が表示されます。
左側のサイドバーに Components/Button が表示され、Primary、Secondary、WithClick の各ストーリーを切り替えて確認できるはずです。
ステップ 7: Turborepo との統合
Turborepo を使って、ビルドと Storybook の起動を一括管理しましょう。
ルートの package.json に以下のスクリプトを追加します(すでに追加済みの場合はスキップしてください)。
json{
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "storybook": "yarn workspace storybook storybook"
  }
}
これにより、yarn build で全パッケージのビルドが、yarn storybook で Storybook の起動が行えます。
また、turbo.json の設定により、依存関係が自動的に解決されるため、UI パッケージのビルドを忘れる心配もありません。
以下の図は、Turborepo のタスク実行フローを示しています。
mermaidflowchart TD
  start["yarn storybook"]
  start --> turbo["Turborepo タスク解決"]
  turbo --> dep_check["依存関係チェック"]
  dep_check --> build_ui["ui パッケージ<br/>ビルド"]
  build_ui --> run_sb["Storybook 起動"]
  run_sb --> done["http://localhost:6006<br/>で閲覧可能"]
このフローにより、常に最新のコンポーネントが Storybook に反映されます。
よくあるエラーと対処法
Monorepo 環境で Storybook を導入する際、以下のようなエラーが発生することがあります。
Error: Cannot find module 'ui'
エラーコード: Error: Cannot find module 'ui'
発生条件: Workspace の依存関係が正しく解決されていない場合に発生します。
解決方法:
- packages/storybook/package.jsonの- dependenciesに- "ui": "workspace:*"が記載されているか確認する
- ルートディレクトリで yarn installを再実行する
- yarn.lockを削除して、再度- yarn installを実行する
これにより、Workspace 間の参照が正しく解決されます。
Error: Build failed - TypeScript compilation errors
エラーコード: Error: Build failed
エラーメッセージ:
luaerror TS2307: Cannot find module 'ui' or its corresponding type declarations.
発生条件: UI パッケージのビルドが完了していない、または型定義が生成されていない場合に発生します。
解決方法:
- yarn workspace ui buildを実行して、UI パッケージをビルドする
- packages/ui/tsconfig.jsonの- declarationが- trueになっているか確認する
- packages/ui/distに- .d.tsファイルが生成されているか確認する
型定義が正しく生成されていれば、エラーは解消されます。
Storybook が起動するが、コンポーネントが表示されない
発生条件: ストーリーファイルのパスが正しく設定されていない場合に発生します。
解決方法:
- packages/storybook/.storybook/main.tsの- storiesフィールドを確認する
- ストーリーファイルが packages/storybook/stories配下に配置されているか確認する
- ファイル名が *.stories.tsxまたは*.stories.tsの形式になっているか確認する
パスとファイル名が正しければ、Storybook が自動的にストーリーを検出します。
さらなる改善ポイント
以下の表は、基本構成からさらに改善できるポイントをまとめたものです。
| # | 改善項目 | 内容 | 効果 | 
|---|---|---|---|
| 1 | CSS-in-JS の導入 | Emotion や styled-components を導入 | スタイル管理の一元化 | 
| 2 | Addon の追加 | a11y、viewport、interactions などのアドオン | テスト・アクセシビリティ向上 | 
| 3 | テーマの共通化 | デザイントークンを別パッケージで管理 | デザインの一貫性向上 | 
| 4 | ビルド最適化 | Vite のキャッシュ設定を調整 | ビルド時間の短縮 | 
| 5 | CI/CD 統合 | GitHub Actions で Storybook を自動デプロイ | チーム共有の効率化 | 
これらの改善により、Storybook の活用範囲がさらに広がります。
まとめ
本記事では、Yarn Workspaces と Turborepo を使った Monorepo 環境に Storybook を導入する手順を、具体的なコード例とともに解説しました。
Monorepo 環境では、依存関係の管理やビルドプロセスの複雑さがネックになりがちですが、適切な設定を行うことで、Storybook をスムーズに統合できます。特に、Workspace の依存関係を明示し、Turborepo のタスク管理を活用することで、開発体験を大きく向上させられるでしょう。
また、エラー発生時の対処法も併せてご紹介しましたので、つまずいた際にはぜひ参考にしてください。Storybook を活用することで、コンポーネントの可視化やチーム間の共有がスムーズになり、開発効率が飛躍的に向上するはずです。
今回ご紹介した構成をベースに、ぜひ皆さんのプロジェクトに合わせてカスタマイズしてみてくださいね。
関連リンク
 article article- Storybook リリース運用:Changesets とバージョン別ドキュメントの整備術
 article article- Storybook 情報設計の教科書:フォルダ/タイトル/ストーリー命名のベストプラクティス
 article article- Storybook Args/ArgTypes 速見表:Controls/Docs/Autodocs を一気に整える
 article article- Storybook を Monorepo に導入:Yarn Workspaces/Turborepo の最短レシピ
 article article- Storybook Builder 徹底比較:Vite vs Webpack vs Rspack の速度と互換性
 article article- Storybook が真っ白!起動しない/ビルド失敗の原因と 15 の対処チェック
 article article- Gemini CLI のコスト監視ダッシュボード:呼び出し数・トークン・失敗率の可視化
 article article- Grok アカウント作成から初回設定まで:5 分で完了するスターターガイド
 article article- FFmpeg コーデック地図 2025:H.264/H.265/AV1/VP9/ProRes/DNxHR の使いどころ
 article article- ESLint の内部構造を覗く:Parser・Scope・Rule・Fixer の連携を図解
 article article- gpt-oss の量子化別ベンチ比較:INT8/FP16/FP8 の速度・品質トレードオフ
 article article- Dify で実現する RAG 以外の戦略:ツール実行・関数呼び出し・自律エージェントの全体像
 blog blog- iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
 blog blog- Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
 blog blog- 【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
 blog blog- Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
 blog blog- Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
 blog blog- フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
 review review- 今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
 review review- ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
 review review- 愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
 review review- 週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
 review review- 新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
 review review- 科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来