storybook-patterns
Component documentation, visual testing, interaction testing
When & Why to Use This Skill
This Claude skill provides comprehensive Storybook 7+ development patterns for building, documenting, and testing UI components. It covers essential techniques including Meta configuration, Story objects, ArgTypes for interactive controls, and advanced features like Play functions for interaction testing and MDX for rich documentation. By following these patterns, developers can ensure consistent component behavior, improve visual regression testing, and create high-quality living style guides for modern web applications.
Use Cases
- Design System Documentation: Creating interactive, self-documenting component libraries using MDX and Autodocs to maintain a single source of truth for UI elements.
- Automated Interaction Testing: Implementing Play functions and user-event simulations to verify component logic and accessibility directly within the Storybook environment.
- Visual Regression & Responsive QA: Utilizing snapshot patterns and responsive viewport configurations to ensure components render correctly across mobile, tablet, and desktop devices.
- Component State Management: Leveraging Args and Controls to dynamically demonstrate and test various component states such as loading, error, and disabled variants.
| name | Storybook Patterns |
|---|---|
| description | Component documentation, visual testing, interaction testing |
Storybook Development Patterns
Component development and documentation with Storybook 7+.
Story Basics
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta = {
title: 'UI/Button',
component: Button,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
backgroundColor: { control: 'color' },
},
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
// Primary story
export const Primary: Story = {
args: {
primary: true,
label: 'Button',
},
};
// Secondary story
export const Secondary: Story = {
args: {
label: 'Button',
},
};
// Large button
export const Large: Story = {
args: {
size: 'large',
label: 'Button',
},
};
Args and Controls
const meta = {
component: Button,
args: {
// Default args for all stories
label: 'Click me',
variant: 'primary'
},
argTypes: {
// Control types
variant: {
control: { type: 'select' },
options: ['primary', 'secondary', 'tertiary'],
},
size: {
control: { type: 'radio' },
options: ['small', 'medium', 'large'],
},
disabled: {
control: 'boolean',
},
onClick: { action: 'clicked' },
},
} satisfies Meta<typeof Button>;
Decorators
// Global decorator
export const decorators = [
(Story) => (
<div style={{ margin: '3em' }}>
<Story />
</div>
),
];
// Component-level decorator
const meta = {
component: Button,
decorators: [
(Story) => (
<ThemeProvider>
<Story />
</ThemeProvider>
),
],
};
Play Functions (Interaction Testing)
import { within, userEvent, expect } from '@storybook/test';
export const FillForm: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Find form elements
const emailInput = canvas.getByLabelText('Email');
const passwordInput = canvas.getByLabelText('Password');
const submitButton = canvas.getByRole('button');
// Interact
await userEvent.type(emailInput, 'user@example.com');
await userEvent.type(passwordInput, 'password123');
await userEvent.click(submitButton);
// Assert
await expect(canvas.getByText('Success')).toBeInTheDocument();
},
};
Visual Testing
Snapshot Tests
export const AllStates: Story = {
render: () => (
<div>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button disabled>Disabled</Button>
</div>
),
};
Responsive Testing
export const Mobile: Story = {
parameters: {
viewport: {
defaultViewport: 'mobile1',
},
},
};
export const Tablet: Story = {
parameters: {
viewport: {
defaultViewport: 'tablet',
},
},
};
Documentation
MDX Stories
import { Meta, Story, Canvas } from '@storybook/blocks';
import * as ButtonStories from './Button.stories';
<Meta of={ButtonStories} />
# Button
Buttons allow users to take actions.
<Canvas of={ButtonStories.Primary} />
## Usage
```tsx
import { Button } from './Button';
<Button variant="primary">Click me</Button>
Variants
```Auto-generated Docs
const meta = {
component: Button,
tags: ['autodocs'], // Auto-generate docs page
parameters: {
docs: {
description: {
component: 'A versatile button component',
},
},
},
};
Loaders
export const WithData: Story = {
loaders: [
async () => ({
user: await fetchUser(),
}),
],
render: (args, { loaded: { user } }) => (
<UserProfile user={user} />
),
};
Best Practices
✅ Do:
- Write stories for all component states
- Use args for interactive controls
- Document usage with MDX
- Test interactions with play functions
- Use decorators for common wrappers
- Keep stories focused and simple
❌ Don't:
- Put business logic in stories
- Duplicate setup across stories
- Skip edge cases and error states
- Forget responsive variants
- Ignore accessibility testing