Storybookを使ってみよう - UIコンポーネントカタログを作る入門

intermediate | 60分 で読める | 2026.04.10

公式ドキュメント

このチュートリアルで学ぶこと

  • Storybookのインストールと初期化
  • CSF (Component Story Format) 3.0 でストーリーを書く
  • Args / ArgTypes / Controls の使い方
  • デコレータでテーマ・状態を切り替える
  • アクセシビリティアドオン (a11y)
  • インタラクションテスト
  • 静的ビルドと公開

StorybookはUIコンポーネントを単独で開発・閲覧・テストするためのツールです。デザインシステム構築やプロパティの組み合わせ確認、E2Eから切り離したコンポーネント単体の品質保証に最適です

前提条件・必要な環境

  • Node.js 20以上
  • React + Viteプロジェクト(無ければ新規作成)
  • TypeScriptの基礎
  • パッケージマネージャ npm / pnpm

新規プロジェクトを作る場合:

npm create vite@latest my-ui -- --template react-ts
cd my-ui
npm install

インストール / セットアップ

Storybookは公式CLIで一発導入できます。プロジェクトルートで実行:

npx storybook@latest init

CLIがプロジェクト構成を検出し、Vite + Reactであれば適切なフレームワークパッケージ (@storybook/react-vite) を自動選択します。

完了後、Storybookを起動:

npm run storybook

ブラウザで http://localhost:6006 が開きます。

追加されるファイル

.storybook/
├── main.ts # Storybook全体設定
└── preview.ts # グローバルパラメータ・デコレータ

src/stories/ # サンプルストーリー

.storybook/main.ts (例):

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

const config: StorybookConfig = {
 stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
 addons: [
 '@storybook/addon-essentials',
 '@storybook/addon-a11y',
 '@storybook/addon-interactions',
 ],
 framework: {
 name: '@storybook/react-vite',
 options: {},
 },
};

export default config;

基本概念

ストーリー(Story)

コンポーネントの「ある状態」を表す関数。1コンポーネントに対し複数のストーリーを書きます。

CSF 3.0

Component Story Formatの最新版。meta のdefault export と、各ストーリーをnamed exportで定義します。

ポイント: CSF 3.0ではmetaのdefault exportと各ストーリーのnamed exportで構成します。Argsでpropsを定義し、Storybook UIからインタラクティブに変更できます。

Args

コンポーネントに渡すpropsの集合。Storybook UIから動的に変更できます。

Decorators

ストーリーをラップしてテーマプロバイダやルーター等を提供する関数。

ステップバイステップ実装

シンプルなButtonコンポーネントを作り、Storybookで管理していきます。

Step 1: Buttonコンポーネントを作る

src/components/Button.tsx:

import type { ButtonHTMLAttributes, ReactNode } from 'react';

export type ButtonVariant = 'primary' | 'secondary' | 'danger';
export type ButtonSize = 'sm' | 'md' | 'lg';

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
 variant?: ButtonVariant;
 size?: ButtonSize;
 children: ReactNode;
}

const variantClass: Record<ButtonVariant, string> = {
 primary: 'bg-blue-600 text-white hover:bg-blue-700',
 secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
 danger: 'bg-red-600 text-white hover:bg-red-700',
};

const sizeClass: Record<ButtonSize, string> = {
 sm: 'px-2 py-1 text-sm',
 md: 'px-4 py-2 text-base',
 lg: 'px-6 py-3 text-lg',
};

export function Button({
 variant = 'primary',
 size = 'md',
 children,
 ...rest
}: ButtonProps) {
 return (
 <button
 className={`rounded font-medium ${variantClass[variant]} ${sizeClass[size]}`}
 {...rest}
 >
 {children}
 </button>
 );
}

Step 2: 最初のストーリー

src/components/Button.stories.tsx:

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

const meta: Meta<typeof Button> = {
 title: 'Components/Button',
 component: Button,
 tags: ['autodocs'],
 argTypes: {
 variant: {
 control: 'select',
 options: ['primary', 'secondary', 'danger'],
 },
 size: {
 control: 'radio',
 options: ['sm', 'md', 'lg'],
 },
 onClick: { action: 'clicked' },
 },
 args: {
 children: 'Click me',
 variant: 'primary',
 size: 'md',
 },
};

export default meta;

type Story = StoryObj<typeof Button>;

export const Primary: Story = {};

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

export const Danger: Story = {
 args: {
 variant: 'danger',
 children: 'Delete',
 },
};

export const Large: Story = {
 args: {
 size: 'lg',
 children: 'Large Button',
 },
};

export const Disabled: Story = {
 args: {
 disabled: true,
 children: 'Disabled',
 },
};

Storybookに切り替えると Components/Button 配下に5つのストーリーが表示されます。Controlsパネルでpropsを動的に変更できます。

Step 3: デコレータでラップ

全ストーリーに共通の余白を付けたいとき、.storybook/preview.ts でグローバルデコレータを追加します。

import type { Preview } from '@storybook/react';
import React from 'react';

const preview: Preview = {
 parameters: {
 actions: { argTypesRegex: '^on[A-Z].*' },
 controls: {
 matchers: {
 color: /(background|color)$/i,
 date: /Date$/,
 },
 },
 },
 decorators: [
 (Story) => (
 React.createElement('div', { style: { padding: 16 } }, React.createElement(Story))
 ),
 ],
};

export default preview;

Step 4: a11yアドオンで自動チェック

@storybook/addon-a11y がインストール済みなら、各ストーリーの下部 Accessibility タブにaxeの結果が表示されます。例えば、Button内のテキストを空にして実行すると、ボタンに名前が無い旨の警告が出ます。

Step 5: インタラクションテスト

@storybook/addon-interactions を活用し、ストーリー内でユーザー操作と検証を記述できます。

import { within, userEvent, expect } from '@storybook/test';

export const Clickable: Story = {
 args: { children: 'Tap me' },
 play: async ({ canvasElement, args }) => {
 const canvas = within(canvasElement);
 const button = canvas.getByRole('button', { name: /tap me/i });
 await userEvent.click(button);
 await expect(button).toBeInTheDocument();
 },
};

Interactions パネルにステップが表示され、再生・巻き戻しできます。

Step 6: MDXでドキュメントを書く

src/components/Button.mdx:

import { Meta, Story, Canvas } from '@storybook/blocks';
import * as ButtonStories from './Button.stories';

<Meta of={ButtonStories} />

# Button

汎用ボタンコンポーネント。デザインシステムの基本要素です。

<Canvas of={ButtonStories.Primary} />

## Variants

<Canvas of={ButtonStories.Secondary} />
<Canvas of={ButtonStories.Danger} />

Step 7: 静的ビルド

npm run build-storybook

storybook-static/ 配下に静的ファイルが出力されます。GitHub PagesやNetlify、ChromaticなどにそのままデプロイできるUIカタログとして公開できます。

完成コード全体

my-ui/
├── .storybook/
│ ├── main.ts
│ └── preview.ts
├── src/
│ └── components/
│ ├── Button.tsx
│ ├── Button.stories.tsx
│ └── Button.mdx
├── package.json
└── vite.config.ts

npm run storybook で開発、npm run build-storybook で静的出力、という流れになります。

よくあるエラーと対処

Cannot find module '@storybook/react-vite'

Storybookの初期化が完了していないか、依存が壊れています。一度クリーンインストールしましょう。

rm -rf node_modules package-lock.json
npm install

スタイルが当たらない (Tailwindの場合)

.storybook/preview.ts でグローバルCSSをimportする必要があります。

import '../src/index.css';

autodocs が表示されない

tags: ['autodocs'] をmetaに設定し、addon-essentials がインストールされていることを確認してください。

型エラー: Meta<typeof Button>

Storybookとフレームワークパッケージのバージョンが揃っていない可能性があります。@storybook/react@storybook/react-vite を同じバージョンに合わせましょう。

ベストプラクティス

  • 1コンポーネント1ストーリーファイル: 関連するバリエーションをまとめる
  • 状態を網羅する: hover/disabled/loading/error などすべて作る
  • デザイントークンを活用: テーマプロバイダをデコレータで適用してダークモード切替を可能に
  • a11y を CIに組み込む: 自動テストでアクセシビリティ違反を検知
  • インタラクションを書く: 単純な表示確認だけでなくクリックや入力もテスト
  • Visual Regression: ChromaticやLoki等で見た目の差分を自動検知
  • モックは軽く: Storybook内でデータをfetchするときは msw-storybook-addon を使う

次のステップ(発展課題)

  1. Form系コンポーネント(Input、Select、Checkbox)を追加してデザインシステム化
  2. ダークモード@storybook/addon-themes で切り替え
  3. Chromaticで Visual Regression を導入
  4. MSWでAPIモック付きのストーリーを書く
  5. Test Runner (@storybook/test-runner) でCI上の自動テスト
  6. i18n対応: 言語切り替えデコレータを作る
  7. Figma連携: @storybook/addon-designs でデザインカンプを並べる

ここまでの整理

実践メモ: a11yアドオンを有効にすると、ストーリー上でアクセシビリティ違反を自動検出できます。早期発見でユーザビリティを向上させましょう。

Storybookを導入すると、コンポーネント開発のフィードバックループが劇的に短縮されます。

  • propの組み合わせを目視で確認できる
  • アクセシビリティ違反を早期発見
  • デザイナーやPMとのコミュニケーション資料になる
  • ドキュメントとテストの両方を兼ねられる

UIライブラリを育てるなら、最初の一歩としてStorybookを入れる価値は十分あります。

補足: 複数フレームワーク対応

Storybookは React 以外にも Vue, Svelte, Angular, Web Components, Solid に対応しています。フレームワークごとに公式パッケージが用意されています。

フレームワークパッケージ
React + Vite@storybook/react-vite
React + Webpack@storybook/react-webpack5
Vue 3 + Vite@storybook/vue3-vite
Svelte + Vite@storybook/svelte-vite
Angular@storybook/angular

npx storybook@latest init がプロジェクトを自動検出して適切なパッケージを選んでくれるので、基本は意識しなくて大丈夫です。

補足: テーマ切り替えデコレータ

ダークモードを切り替えたい場合、@storybook/addon-themes を導入してデコレータを設定します。

npm install -D @storybook/addon-themes

.storybook/preview.ts:

import { withThemeByClassName } from '@storybook/addon-themes';

export const decorators = [
 withThemeByClassName({
 themes: {
 light: 'light',
 dark: 'dark',
 },
 defaultTheme: 'light',
 }),
];

ツールバーからテーマを切り替えてストーリーが正しく追従するか確認できます。

補足: CIでの自動テスト

@storybook/test-runner を使えば、ヘッドレスブラウザで全ストーリーをスモークテストできます。

npm install -D @storybook/test-runner

package.json:

{
 "scripts": {
 "test-storybook": "test-storybook"
 }
}

GitHub Actions の例:

- run: npm run build-storybook
- run: npx http-server storybook-static --port 6006 &
- run: npx wait-on http://127.0.0.1:6006
- run: npm run test-storybook

各ストーリーがレンダリングできるか、play 関数のインタラクションが成功するかを CI でチェックできます。

補足: モックAPIとの連携

ネットワーク呼び出しを伴うコンポーネントをStorybookで動かすには、msw-storybook-addon が便利です。

npm install -D msw msw-storybook-addon

ストーリーごとに別のレスポンスを返すよう設定でき、ローディング・成功・エラー状態をすべてカタログ化できます

まとめ

この記事では、手順を追って動かしながら重要な確認ポイントを押さえることが大切です。まずは学習用の最小構成で動作確認し、本番利用する前にセキュリティ、権限、エラー処理、運用手順を見直してください。

参考リソース

← 一覧に戻る
PR
PR
PR
PR