storybook-patterns

mcgilly17's avatarfrom mcgilly17

Component documentation, visual testing, interaction testing

⭐1stars🔀1forks📁View on GitHub🕐Updated Jan 3, 2026

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.
nameStorybook Patterns
descriptionComponent 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