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

intermediate | 60分 で読める | 2026.04.10

公式ドキュメント

この記事の要点

• StorybookのインストールとCSF 3.0でのストーリー記述
• Args・Controls・デコレータによるインタラクティブな開発
• アクセシビリティアドオンと静的ビルドの活用

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

  • 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';



# Button

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



## Variants



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

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

参考リソース

この技術を体系的に学びたいですか?

未来学では東証プライム上場企業のITエンジニアが24時間サポート。月額24,800円から、退会金0円のオンラインIT塾です。

メールで無料相談する
← 一覧に戻る