Skip to content
Design

Storybook Setup Guide

Set up Storybook for React — addons, testing, documentation, and CI integration.

stellae.design

9 min read

Introduction

Storybook is the industry standard for developing, documenting, and testing UI components in isolation. It gives you a sandbox where you can render every component variant, interact with it, and verify its behavior without running your full application.

For design systems, Storybook serves triple duty: it's a development environment, a documentation site, and a visual testing platform. With the right addons, it becomes the central hub for your component library.

This guide covers setting up Storybook with React, essential addons, writing effective stories, visual testing, and deploying Storybook as documentation.


Key Concepts

Installation and Configuration

typescript
# Initialize Storybook in your project
npx storybook@latest init

# .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(ts|tsx)'],
  addons: [
    '@storybook/addon-essentials',     // Controls, actions, viewport, docs
    '@storybook/addon-a11y',           // Accessibility checks
    '@storybook/addon-interactions',   // Interaction testing
  ],
  framework: '@storybook/react-vite',
  docs: { autodocs: 'tag' },
};
export default config;

Writing Stories

typescript
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  title: 'Components/Button',
  component: Button,
  tags: ['autodocs'], // Generate docs page
  argTypes: {
    variant: { control: 'select', options: ['primary', 'secondary', 'ghost', 'destructive'] },
    size: { control: 'select', options: ['sm', 'md', 'lg'] },
  },
};
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', variant: 'secondary' } };
export const Loading: Story = { args: { children: 'Loading...', isLoading: true } };
export const AllVariants: Story = {
  render: () => (
    <div className="flex gap-4">
      <Button variant="primary">Primary</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="ghost">Ghost</Button>
      <Button variant="destructive">Destructive</Button>
    </div>
  ),
};

Practical Examples

1. Interaction Testing

typescript
import { within, userEvent, expect } from '@storybook/test';

export const FormSubmission: Story = {
  render: () => <ContactForm />,
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    await userEvent.type(canvas.getByLabelText('Name'), 'John Doe');
    await userEvent.type(canvas.getByLabelText('Email'), 'john@example.com');
    await userEvent.click(canvas.getByRole('button', { name: 'Submit' }));
    await expect(canvas.getByText('Success!')).toBeInTheDocument();
  },
};

2. Accessibility Addon

typescript
// The a11y addon automatically runs axe-core on every story
// Add it to .storybook/main.ts addons array
// '@storybook/addon-a11y'

// Override rules per story if needed
export const WithAccessibilityOverride: Story = {
  parameters: {
    a11y: {
      config: {
        rules: [{ id: 'color-contrast', enabled: false }],
      },
    },
  },
};

3. Deploying Storybook to Chromatic

bash
# Install Chromatic
npm install chromatic --save-dev

# Add to CI
# .github/workflows/storybook.yml
- name: Publish to Chromatic
  uses: chromaui/action@latest
  with:
    projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
    buildScriptName: build-storybook

Best Practices

  • ✅ Write stories for every component variant and state
  • ✅ Use autodocs for automatic documentation generation
  • ✅ Add the a11y addon and fix all violations
  • ✅ Use interaction tests for complex component behavior
  • ✅ Deploy Storybook as your component documentation site
  • ❌ Don't write stories that only render the default state — cover edge cases
  • ❌ Don't skip mobile viewport stories — test responsive behavior

Common Pitfalls

  • Stories that don't match real usage — write stories that reflect how consumers actually use components
  • Missing decorator for theme/context providers — components that need context will break
  • Not testing dark mode — add a theme switcher toolbar addon
  • Storybook build errors in CI — run storybook build locally before pushing

Designdesign

Was this article helpful?