Storybook入門:React TypeScriptでコンポーネントのStoryを書く
Storybookとは
StorybookはUIコンポーネントをアプリから切り離して、独立した環境で開発・確認できるツールです。
通常の開発:アプリを起動 → 特定の画面まで遷移 → コンポーネントを確認 Storybook:コンポーネントを直接開いてpropsを変えながら確認
- コンポーネントの全状態(バリアント・エラー・空状態など)を一覧できる
- デザイナーとの確認・ドキュメントとして使える
- React・Vue・Angularなど主要フレームワークに対応
セットアップ
Vite + React + TypeScript のプロジェクトに追加します。
npm create vite@latest my-app -- --template react-ts cd my-app npm install npx storybook@latest init
storybook init がプロジェクトを自動検出して必要なパッケージをインストールします。完了後に起動します。
npm run storybook # http://localhost:6006 でStorybookが開く
コンポーネントを作る
例として Button コンポーネントを用意します。
// src/components/Button.tsx type ButtonProps = { label: string variant?: 'primary' | 'secondary' size?: 'small' | 'medium' | 'large' disabled?: boolean onClick?: () => void } export function Button({ label, variant = 'primary', size = 'medium', disabled = false, onClick, }: ButtonProps) { const base = 'px-4 py-2 rounded font-medium' const variants = { primary: 'bg-blue-600 text-white hover:bg-blue-700', secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300', } const sizes = { small: 'text-sm px-3 py-1', medium: 'text-base', large: 'text-lg px-6 py-3', } return ( <button className={`${base} ${variants[variant]} ${sizes[size]}`} disabled={disabled} onClick={onClick} > {label} </button> ) }
Storyを書く(CSF3形式)
Story ファイルはコンポーネントと同じディレクトリに .stories.tsx として置きます。
// src/components/Button.stories.tsx import type { Meta, StoryObj } from '@storybook/react' import { Button } from './Button' const meta: Meta<typeof Button> = { component: Button, title: 'Components/Button', } export default meta type Story = StoryObj<typeof Button> export const Primary: Story = { args: { label: '送信する', variant: 'primary', }, } export const Secondary: Story = { args: { label: 'キャンセル', variant: 'secondary', }, } export const Large: Story = { args: { label: '大きいボタン', size: 'large', }, } export const Disabled: Story = { args: { label: '送信できません', disabled: true, }, }
args に渡したオブジェクトがそのままコンポーネントのpropsになります。Storybook上でControls(UIパネル)から値を変えて動作確認もできます。
Meta の主なオプション
const meta: Meta<typeof Button> = { component: Button, title: 'Components/Button', // サイドバーの階層(省略可) args: { // 全Storyに共通のデフォルトargs label: 'ボタン', }, argTypes: { // Controlsパネルの表示設定 variant: { control: 'select', options: ['primary', 'secondary'], }, onClick: { action: 'clicked' }, // クリックをActionsパネルに表示 }, }
| プロパティ | 説明 |
|---|---|
component |
対象コンポーネント(必須) |
title |
サイドバーの表示パス。'Forms/Button' のように階層化できる |
args |
全Storyに適用するデフォルトprops |
argTypes |
Controlsの入力タイプや説明を設定 |
Storyの継承(argsの上書き)
meta.args で共通のデフォルトを設定し、各Storyで上書きできます。
const meta: Meta<typeof Button> = { component: Button, args: { label: 'ボタン', // 全Storyのデフォルト size: 'medium', }, } export const Primary: Story = { args: { variant: 'primary' }, // labelとsizeはmetaから継承 } export const Secondary: Story = { args: { variant: 'secondary' }, }
ファイル構成
src/
components/
Button.tsx
Button.stories.tsx ← Storyファイル
Input.tsx
Input.stories.tsx
コンポーネントと同じディレクトリに置くのが一般的です。.storybook/main.ts の stories に '../src/**/*.stories.tsx' が設定されており、このパターンにマッチするファイルが自動で読み込まれます。
まとめ
| 概念 | 説明 |
|---|---|
| Story | コンポーネントの1つの状態を記述したもの |
args |
Storyに渡すprops。ControlsパネルからGUIで変更できる |
Meta |
コンポーネント全体の設定(title, args, argTypesなど) |
StoryObj |
TypeScriptでStoryを型安全に定義するための型 |
export default metaでコンポーネントを登録し、export const XXX: Storyで各状態を定義するargsでpropsを渡すと、ControlsパネルからGUIで変更して動作確認できるmeta.argsに共通のデフォルトを置き、各StoryのargsでそれぞれのVariantを表現するのが基本パターン
Storybookで確認したコンポーネントの動作をさらにデバッグしたい場合は「React DevTools Components:コンポーネントの状態をリアルタイムで確認する」も参照してください。
アプリ全体のE2Eテストには「Playwright入門:TypeScriptでE2Eテストを書く」も参照してください。