Storybook Complete Guide | UI Component Development Environment
이 글의 핵심
Storybook is a frontend workshop for building UI components in isolation. It provides a sandbox to develop, test, and document components independently from your app.
Introduction
Storybook is an open-source tool for developing UI components in isolation. It allows you to build and test components outside your app, making development faster and more reliable.
Why Storybook?
Traditional development:
# Need to:
1. Start entire app
2. Navigate to component
3. Set up specific state
4. Test edge cases manually
5. Repeat for each component
With Storybook:
# Just:
1. Run Storybook
2. See all components
3. Test all states instantly
4. Document automatically
1. Installation
New Project
npx storybook@latest init
Storybook automatically detects your framework and configures itself!
Existing React Project
npx storybook@latest init
# Start Storybook
npm run storybook
2. Writing Stories
Basic Story
// Button.tsx
export interface ButtonProps {
label: string;
onClick?: () => void;
variant?: 'primary' | 'secondary';
}
export function Button({ label, onClick, variant = 'primary' }: ButtonProps) {
return (
<button className={`btn btn-${variant}`} onClick={onClick}>
{label}
</button>
);
}
// 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'],
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: {
label: 'Button',
variant: 'primary',
},
};
export const Secondary: Story = {
args: {
label: 'Button',
variant: 'secondary',
},
};
Interactive Stories
export const WithClick: Story = {
args: {
label: 'Click me',
onClick: () => alert('Clicked!'),
},
};
3. Component States
// UserCard.stories.tsx
import { UserCard } from './UserCard';
export default {
title: 'Components/UserCard',
component: UserCard,
};
export const Default = {
args: {
name: 'Alice',
role: 'Developer',
avatar: 'https://i.pravatar.cc/150?img=1',
},
};
export const NoAvatar = {
args: {
name: 'Bob',
role: 'Designer',
},
};
export const LongName = {
args: {
name: 'Christopher Alexander Johnson',
role: 'Senior Software Engineer',
},
};
export const Loading = {
args: {
isLoading: true,
},
};
export const Error = {
args: {
error: 'Failed to load user',
},
};
4. Args and Controls
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger'],
},
size: {
control: 'radio',
options: ['small', 'medium', 'large'],
},
disabled: {
control: 'boolean',
},
label: {
control: 'text',
},
},
};
export default meta;
Now you can change args in UI!
5. Decorators
Global Decorator
// .storybook/preview.tsx
import { ThemeProvider } from '../src/theme';
export const decorators = [
(Story) => (
<ThemeProvider>
<Story />
</ThemeProvider>
),
];
Story-specific Decorator
export const Primary: Story = {
args: { label: 'Button' },
decorators: [
(Story) => (
<div style={{ padding: '3rem' }}>
<Story />
</div>
),
],
};
6. Addons
Essential Addons
npm install --save-dev @storybook/addon-essentials
Includes:
- Docs - Auto-generated documentation
- Controls - Interactive args editing
- Actions - Event logging
- Viewport - Responsive preview
- Backgrounds - Test on different backgrounds
- Toolbars - Custom toolbar items
a11y Addon
npm install --save-dev @storybook/addon-a11y
// .storybook/main.ts
export default {
addons: ['@storybook/addon-a11y'],
};
Tests components for accessibility issues!
Interactions Addon
npm install --save-dev @storybook/addon-interactions
import { userEvent, within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
export const WithInteraction: Story = {
args: { label: 'Click me' },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByRole('button');
await userEvent.click(button);
await expect(button).toHaveTextContent('Clicked!');
},
};
7. Documentation
Auto-generated Docs
import type { Meta } from '@storybook/react';
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
tags: ['autodocs'], // Enable auto docs
parameters: {
docs: {
description: {
component: 'A button component with multiple variants.',
},
},
},
};
MDX Documentation
{/* Button.stories.mdx */}
import { Meta, Story, Canvas } from '@storybook/blocks';
import { Button } from './Button';
<Meta title="Components/Button" component={Button} />
# Button
A flexible button component.
## Usage
<Canvas>
<Story name="Primary">
<Button label="Click me" variant="primary" />
</Story>
</Canvas>
## Props
- `label` - Button text
- `variant` - Button style (primary, secondary, danger)
- `onClick` - Click handler
8. Testing
Interaction Testing
export const LoginForm: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Fill form
await userEvent.type(canvas.getByLabelText('Email'), '[email protected]');
await userEvent.type(canvas.getByLabelText('Password'), 'password123');
// Submit
await userEvent.click(canvas.getByRole('button', { name: /submit/i }));
// Assert
await expect(canvas.getByText('Success!')).toBeInTheDocument();
},
};
Visual Regression Testing
npm install --save-dev @storybook/test-runner chromatic
# Run tests
npm run test-storybook
# Visual testing with Chromatic
npx chromatic --project-token=<token>
9. Deployment
Build Static Storybook
npm run build-storybook
Deploy to GitHub Pages
# .github/workflows/storybook.yml
name: Deploy Storybook
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm run build-storybook
- uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./storybook-static
10. Best Practices
1. One Component Per Story File
components/
├── Button/
│ ├── Button.tsx
│ ├── Button.stories.tsx
│ ├── Button.test.tsx
│ └── Button.module.css
2. Cover All States
export const Loading: Story = { args: { isLoading: true } };
export const Error: Story = { args: { error: 'Failed' } };
export const Empty: Story = { args: { data: [] } };
export const WithData: Story = { args: { data: [...] } };
3. Use Args for Reusability
// Good: reusable
export const Primary: Story = {
args: { variant: 'primary' }
};
// Bad: hardcoded
export const Primary: Story = {
render: () => <Button variant="primary" />
};
Summary
Storybook transforms UI development:
- Isolated development - build components independently
- Interactive testing - test all states easily
- Auto documentation - generate component docs
- Visual testing - catch UI regressions
- Team collaboration - share component library
Key Takeaways:
- Develop components in isolation
- Test all states and edge cases
- Auto-generate documentation
- Run interaction tests
- Deploy for team collaboration
Next Steps:
- Test with Testing Library
- Visual test with Chromatic
- Build Design System
Resources: