Agent skill
storybook
Develops and documents UI components in isolation with Storybook's interactive workshop environment. Use when building component libraries, documenting design systems, or when user mentions Storybook, component documentation, or UI development.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/storybook
SKILL.md
Storybook
Industry-standard workshop for building, documenting, and testing UI components in isolation.
Quick Start
# Install in existing project
npx storybook@latest init
# Start Storybook
npm run storybook
Project Structure
.storybook/
main.ts # Configuration
preview.ts # Global decorators and parameters
preview-head.html # Custom head content
src/
components/
Button/
Button.tsx
Button.stories.tsx
Button.module.css
Configuration
main.ts
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-onboarding',
'@storybook/addon-essentials',
'@chromatic-com/storybook',
'@storybook/addon-interactions',
'@storybook/addon-a11y',
],
framework: {
name: '@storybook/react-vite',
options: {},
},
docs: {
autodocs: 'tag',
},
staticDirs: ['../public'],
typescript: {
reactDocgen: 'react-docgen-typescript',
},
};
export default config;
preview.ts
// .storybook/preview.ts
import type { Preview } from '@storybook/react';
import '../src/styles/globals.css';
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
backgrounds: {
default: 'light',
values: [
{ name: 'light', value: '#ffffff' },
{ name: 'dark', value: '#1a1a1a' },
{ name: 'gray', value: '#f5f5f5' },
],
},
layout: 'centered',
},
decorators: [
(Story) => (
<div style={{ margin: '1rem' }}>
<Story />
</div>
),
],
globalTypes: {
theme: {
name: 'Theme',
description: 'Global theme',
defaultValue: 'light',
toolbar: {
icon: 'circlehollow',
items: ['light', 'dark'],
showName: true,
},
},
},
};
export default preview;
Writing Stories
Basic Story
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger'],
},
size: {
control: 'radio',
options: ['sm', 'md', 'lg'],
},
onClick: { action: 'clicked' },
},
};
export default meta;
type Story = StoryObj<typeof meta>;
// Stories
export const Primary: Story = {
args: {
variant: 'primary',
children: 'Button',
},
};
export const Secondary: Story = {
args: {
variant: 'secondary',
children: 'Button',
},
};
export const Large: Story = {
args: {
size: 'lg',
children: 'Large Button',
},
};
export const Disabled: Story = {
args: {
disabled: true,
children: 'Disabled',
},
};
With Decorators
export const InDarkMode: Story = {
decorators: [
(Story) => (
<div className="dark bg-gray-900 p-4">
<Story />
</div>
),
],
args: {
children: 'Dark Mode Button',
},
};
// Decorator with args access
export const WithContext: Story = {
decorators: [
(Story, context) => (
<ThemeProvider theme={context.globals.theme}>
<Story />
</ThemeProvider>
),
],
};
With Play Functions (Interaction Testing)
import { within, userEvent, expect } from '@storybook/test';
export const Filled: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Type in input
const input = canvas.getByPlaceholderText('Enter email');
await userEvent.type(input, 'test@example.com', { delay: 100 });
// Click button
const button = canvas.getByRole('button', { name: /submit/i });
await userEvent.click(button);
// Assert
await expect(canvas.getByText('Success!')).toBeInTheDocument();
},
};
export const WithValidation: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Submit empty form
await userEvent.click(canvas.getByRole('button'));
// Check for error
await expect(canvas.getByText('Email is required')).toBeVisible();
},
};
Async Stories
export const WithData: Story = {
loaders: [
async () => ({
users: await fetch('/api/users').then((r) => r.json()),
}),
],
render: (args, { loaded: { users } }) => (
<UserList users={users} {...args} />
),
};
// Or with render function
export const Loading: Story = {
render: () => {
const [data, setData] = useState(null);
useEffect(() => {
fetchData().then(setData);
}, []);
if (!data) return <Skeleton />;
return <Component data={data} />;
},
};
Controls
ArgTypes
const meta: Meta<typeof Button> = {
argTypes: {
// Control types
variant: {
control: 'select',
options: ['primary', 'secondary'],
description: 'Visual style variant',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'primary' },
},
},
size: {
control: 'inline-radio',
options: ['sm', 'md', 'lg'],
},
disabled: {
control: 'boolean',
},
count: {
control: { type: 'number', min: 0, max: 100, step: 1 },
},
color: {
control: 'color',
},
date: {
control: 'date',
},
items: {
control: 'object',
},
label: {
control: 'text',
},
// Disable control
onClick: {
control: false,
},
// Categorize in table
theme: {
table: {
category: 'Styling',
},
},
},
};
Documentation
MDX Documentation
{/* Button.mdx */}
import { Canvas, Meta, Story, Controls, ArgTypes } from '@storybook/blocks';
import * as ButtonStories from './Button.stories';
<Meta of={ButtonStories} />
# Button
Buttons trigger actions when clicked.
## Usage
```tsx
import { Button } from '@/components/Button';
<Button variant="primary" onClick={handleClick}>
Click me
</Button>
Examples
Primary Button
All Variants
Props
Component Documentation
// Button.tsx - JSDoc for autodocs
interface ButtonProps {
/** The visual style variant */
variant?: 'primary' | 'secondary' | 'danger';
/** Button size */
size?: 'sm' | 'md' | 'lg';
/** Whether the button is disabled */
disabled?: boolean;
/** Click handler */
onClick?: () => void;
/** Button content */
children: React.ReactNode;
}
/**
* Primary UI component for user interaction.
*
* @example
* ```tsx
* <Button variant="primary" onClick={handleClick}>
* Click me
* </Button>
* ```
*/
export function Button({ variant = 'primary', size = 'md', ...props }: ButtonProps) {
// ...
}
Addons
Essential Addons (Included)
// Already included with @storybook/addon-essentials:
// - Controls: Interactive props
// - Actions: Event logging
// - Docs: Auto-documentation
// - Viewport: Responsive testing
// - Backgrounds: Background colors
// - Toolbars: Global controls
// - Measure: Layout measurement
// - Outline: DOM outlines
Accessibility Testing
npm install -D @storybook/addon-a11y
// .storybook/main.ts
addons: ['@storybook/addon-a11y'],
// In story
export const Accessible: Story = {
parameters: {
a11y: {
config: {
rules: [{ id: 'color-contrast', enabled: true }],
},
},
},
};
Interactions
npm install -D @storybook/addon-interactions @storybook/test
import { within, userEvent, expect, fn } from '@storybook/test';
const meta: Meta<typeof Form> = {
args: {
onSubmit: fn(),
},
};
export const Submitted: Story = {
play: async ({ args, canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.type(canvas.getByLabelText('Email'), 'test@test.com');
await userEvent.click(canvas.getByRole('button'));
await expect(args.onSubmit).toHaveBeenCalled();
},
};
Testing Stories
With Test Runner
npm install -D @storybook/test-runner
# Run tests
npx test-storybook
# Watch mode
npx test-storybook --watch
With Vitest
// Button.test.tsx
import { composeStories } from '@storybook/react';
import { render, screen } from '@testing-library/react';
import * as stories from './Button.stories';
const { Primary, Secondary } = composeStories(stories);
test('Primary button renders correctly', () => {
render(<Primary />);
expect(screen.getByRole('button')).toHaveClass('btn-primary');
});
test('runs play function', async () => {
const { container } = render(<Secondary />);
await Secondary.play?.({ canvasElement: container });
});
Visual Testing with Chromatic
# Install
npm install -D chromatic
# Run
npx chromatic --project-token=<token>
# .github/workflows/chromatic.yml
name: Chromatic
on: push
jobs:
chromatic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
- run: npm ci
- uses: chromaui/action@latest
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
Build and Deploy
# Build static Storybook
npm run build-storybook
# Output in storybook-static/
# Deploy to GitHub Pages
# .github/workflows/deploy.yml
name: Deploy Storybook
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci
- run: npm run build-storybook
- uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./storybook-static
Reference Files
- patterns.md - Common Storybook patterns
- testing.md - Testing strategies with Storybook
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?