storybook
Storybook
Build and document UI components in isolation with interactive stories.
Instructions
- Write stories for all components - Each component should have a story file
- Use args for props - Define props through args for interactive controls
- Add decorators - Wrap stories with providers and layout wrappers
- Document with MDX - Write component documentation alongside stories
- Test interactions - Use play functions for interaction testing
Setup
# Initialize Storybook in existing project
npx storybook@latest init
# Start Storybook
npm run storybook
Configuration
// .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-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-a11y',
],
framework: {
name: '@storybook/react-vite',
options: {},
},
docs: {
autodocs: 'tag',
},
};
export default config;
Writing Stories
Basic Story Structure
// 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'],
parameters: {
layout: 'centered',
},
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger', 'ghost'],
description: 'The visual style of the button',
},
size: {
control: 'radio',
options: ['sm', 'md', 'lg'],
},
disabled: {
control: 'boolean',
},
onClick: { action: 'clicked' },
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: {
children: 'Primary Button',
variant: 'primary',
},
};
export const Secondary: Story = {
args: {
children: 'Secondary Button',
variant: 'secondary',
},
};
export const Disabled: Story = {
args: {
children: 'Disabled Button',
disabled: true,
},
};
Multiple Variants
export const AllVariants: Story = {
render: () => (
<div className="flex flex-wrap gap-4">
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Danger</Button>
<Button variant="ghost">Ghost</Button>
</div>
),
};
export const AllSizes: Story = {
render: () => (
<div className="flex items-center gap-4">
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
</div>
),
};
With Args
// args allow interactive control in Storybook UI
export const Playground: Story = {
args: {
children: 'Click me',
variant: 'primary',
size: 'md',
disabled: false,
loading: false,
},
};
Decorators
Global Decorators
// .storybook/preview.tsx
import type { Preview } from '@storybook/react';
import { ThemeProvider } from '../src/providers/ThemeProvider';
import '../src/styles/globals.css';
const preview: Preview = {
decorators: [
(Story) => (
<ThemeProvider>
<div className="p-4">
<Story />
</div>
</ThemeProvider>
),
],
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;
Story-Level Decorators
export const WithDarkBackground: Story = {
decorators: [
(Story) => (
<div className="bg-gray-900 p-8">
<Story />
</div>
),
],
args: {
children: 'Dark Mode Button',
variant: 'primary',
},
};
Interaction Testing
Play Functions
import { within, userEvent, expect } from '@storybook/test';
export const ClickInteraction: Story = {
args: {
children: 'Click me',
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByRole('button', { name: /click me/i });
await userEvent.click(button);
// Assertions
await expect(button).toBeVisible();
},
};
export const FormSubmission: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Fill form fields
await userEvent.type(
canvas.getByLabelText(/email/i),
'user@example.com'
);
await userEvent.type(
canvas.getByLabelText(/password/i),
'password123'
);
// Submit form
await userEvent.click(canvas.getByRole('button', { name: /submit/i }));
// Check result
await expect(canvas.getByText(/success/i)).toBeInTheDocument();
},
};
Component Documentation
MDX Documentation
{/* Button.mdx */}
import { Meta, Story, Canvas, Controls, Source } from '@storybook/blocks';
import * as ButtonStories from './Button.stories';
<Meta of={ButtonStories} />
# Button
Buttons trigger actions or navigation. Use the appropriate variant
for the action's importance and context.
## Usage Guidelines
- Use **Primary** for the main action on a page
- Use **Secondary** for less important actions
- Use **Danger** only for destructive actions
- Limit to one primary button per section
## Examples
### Default
<Canvas of={ButtonStories.Primary} />
### All Variants
<Canvas of={ButtonStories.AllVariants} />
## Props
<Controls />
## Accessibility
- Buttons have proper focus states
- Loading state disables interaction and shows spinner
- Use `aria-label` for icon-only buttons
## Code
<Source of={ButtonStories.Primary} />
Inline Stories in MDX
import { Canvas, Meta } from '@storybook/blocks';
import { Button } from './Button';
<Meta title="Guide/Button Usage" />
# Using Buttons
Here's how to use buttons in different contexts:
<Canvas>
<Button variant="primary">Save Changes</Button>
<Button variant="secondary">Cancel</Button>
</Canvas>
Parameters
Layout
export const Centered: Story = {
parameters: {
layout: 'centered', // centered | fullscreen | padded
},
};
Backgrounds
export const OnDark: Story = {
parameters: {
backgrounds: {
default: 'dark',
},
},
};
Viewport
export const Mobile: Story = {
parameters: {
viewport: {
defaultViewport: 'mobile1',
},
},
};
Addons
Accessibility Addon
// Automatically checks accessibility
export const AccessibleButton: Story = {
args: {
children: 'Accessible',
'aria-label': 'Accessible button description',
},
parameters: {
a11y: {
config: {
rules: [
{ id: 'color-contrast', enabled: true },
],
},
},
},
};
Actions Addon
const meta: Meta<typeof Button> = {
component: Button,
argTypes: {
onClick: { action: 'clicked' },
onFocus: { action: 'focused' },
onBlur: { action: 'blurred' },
},
};
Project Structure
src/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.test.tsx
│ │ ├── Button.stories.tsx
│ │ ├── Button.mdx
│ │ └── index.ts
│ ├── Card/
│ │ ├── Card.tsx
│ │ ├── Card.stories.tsx
│ │ └── index.ts
│ └── index.ts
├── .storybook/
│ ├── main.ts
│ └── preview.tsx
└── package.json
Best Practices
| Practice | Recommendation |
|---|---|
| File naming | Component.stories.tsx alongside component |
| Story naming | Use descriptive names like WithIcon, Disabled |
| Args | Prefer args over inline JSX for flexibility |
| Decorators | Use for providers, layout, theming |
| Play functions | Test interactions, not just rendering |
| Documentation | Write MDX docs for complex components |
When to Use
- Component library development
- Design system documentation
- Visual regression testing
- Isolated component development
- Team collaboration on UI
- Accessibility testing
Notes
- Storybook 7+ uses Component Story Format (CSF) 3
- Works with React, Vue, Angular, Svelte, and more
- Integrates with Chromatic for visual testing
- Supports TypeScript out of the box
- Hot module reloading for fast development
More from housegarofalo/claude-code-base
mqtt-iot
Configure MQTT brokers (Mosquitto, EMQX) for IoT messaging, device communication, and smart home integration. Manage topics, QoS levels, authentication, and bridging. Use when setting up IoT messaging, smart home communication, or device-to-cloud connectivity. (project)
22devops-engineer-agent
Infrastructure and DevOps specialist. Manages Docker, Kubernetes, CI/CD pipelines, and cloud deployments. Expert in GitHub Actions, Azure DevOps, Terraform, and container orchestration. Use for deployment automation, infrastructure setup, or CI/CD optimization.
6home-assistant
Ultimate Home Assistant skill - complete administration, wireless protocols (Zigbee/ZHA/Z2M, Z-Wave JS, Thread, Matter), ESPHome device building, advanced troubleshooting, performance optimization, security hardening, custom integration development, and professional dashboard design. Covers configuration, REST API, automation debugging, database optimization, SSL/TLS, Jinja2 templating, and HACS custom cards. Use for any HA task.
6power-automate
Expert guidance for Power Automate development including cloud flows, desktop flows, Dataverse connector, expression functions, custom connectors, error handling, and child flow patterns. Use when building automated workflows, writing flow expressions, creating custom connectors from OpenAPI, or implementing error handling patterns.
5mobile-pwa
Build Progressive Web Apps with offline support, push notifications, and native-like experiences. Covers service workers, Web App Manifest, caching strategies, IndexedDB, background sync, and installability. Use for mobile-first web apps, offline-capable applications, and app-like experiences.
5vitest
Fast Vite-native unit testing framework for JavaScript/TypeScript. ESM-first, Jest-compatible API, with instant HMR. Use for modern frontend testing, Vue/React/Svelte testing, or fast test execution. Triggers on vitest, vite test, vue test, svelte test.
5