UI Development

Storybook

Storybook is a workshop for UI components. Build and test them in isolation, document every state, and catch accessibility and visual regressions before they reach users.

intermediate12 min readUpdated Sep 15, 2026
Button.stories.jsx
jsx
// Button.stories.jsx
import { Button } from "./Button";

export default {
  title: "Components/Button",
  component: Button,
  args: { children: "Save" },
};

export const Primary = {
  args: { variant: "primary" },
};

export const Disabled = {
  args: { disabled: true },
};
What it is
A component workshop
Unit of work
A story
Inputs
args and controls
Docs
Autodocs
Testing
Interaction and a11y addons
Frameworks
React, Vue, Svelte, Angular

Why it matters

Why teams adopt Storybook

Build in isolation

Develop and review a component on its own, without running the whole app or navigating to the right page.

Catch a11y issues early

The accessibility addon audits each story and reports violations while you work, before they reach users.

Test every state

Encode loading, empty, error and edge cases as stories, then run interaction and visual checks over them.

The big picture

The three ideas behind Storybook

Stories as isolated states, args as live inputs, and addons that extend the workshop into testing and docs.

Stories

States

Each story renders a component in one meaningful state, with a name that documents its purpose.

Args

Inputs

The props a story passes, editable live through the controls panel.

Addons

Extend

Accessibility, interaction testing, visual regression and documentation plug into the same workshop.

Storybook at a glance

The core of Storybook

Story files

Colocate a .stories file with the component and export one story per state.

Args

Define default props at the meta level and override them per story.

Controls

Tweak args live in the UI to explore a component without editing code.

Decorators

Wrap stories with providers, themes or layout for consistent context.

Interaction tests

Play functions simulate clicks and typing, then assert on the result.

Accessibility addon

Automatic audits of every story for common violations.

A short history

From internal tool to industry standard

  1. 2016

    Storybook released

    A React-specific tool for developing components in isolation.

    16
  2. 2018

    Framework support

    Vue, Angular and others gain official support as adoption grows.

    18
  3. 2020

    Args and Controls

    A simpler story format with live-editable props becomes the default.

    20
  4. 2022

    Interaction testing

    Play functions and the test runner bring testing into the workshop.

    22
  5. Today

    An industry standard

    Used for documentation, accessibility checks and visual regression across major design systems.

    Today

The complete guide

Storybook: Everything you need to know

What is Storybook?

Storybook is a workshop for UI components. It renders a component in isolation, outside your application, so you can develop it without navigating to the right page or satisfying the right conditions. Each state you care about becomes a story, and the collection of stories becomes living documentation.

It started as a development tool and grew into much more: an accessibility auditor, an interaction-testing surface and a visual-regression target. For teams building design systems or shared component libraries, it is often the single most valuable front-end tool after the framework itself.

Stories

A story file exports a default meta object and one named export per state.

// Button.stories.jsx
import { Button } from "./Button";

export default {
  title: "Components/Button",
  component: Button,
  args: { children: "Save" },
  argTypes: {
    variant: {
      control: "select",
      options: ["primary", "secondary", "ghost"],
    },
  },
};

export const Primary = {
  args: { variant: "primary" },
};

export const Secondary = {
  args: { variant: "secondary" },
};

export const Disabled = {
  args: { disabled: true },
};

The title determines where the component appears in the sidebar. The component links the story to the component so Storybook can infer props and generate documentation. Every named export is a story.

Args and controls

Args are the props a story passes to the component. Storybook turns them into a controls panel, so you can change values live without editing code.

// Card.stories.jsx
export default {
  title: "Components/Card",
  component: Card,
  args: {
    title: "Pro plan",
    description: "Everything you need to ship.",
    elevated: false,
  },
};

Because args are structured data, they also drive autodocs, can be shared between stories, and can be reused by interaction tests. Defining markup inline in a render function works but loses the live controls and the single source of truth, so prefer args.

Decorators and context

Some components need a provider, a theme or a layout wrapper. Decorators supply that context around a story.

// preview.jsx
export default {
  decorators: [
    (Story) => (
      <ThemeProvider theme="dark">
        <div style={{ padding: "1rem" }}>
          <Story />
        </div>
      </ThemeProvider>
    ),
  ],
};

A decorator can be global, per-file or per-story, which makes it easy to review a component in different themes or locales without changing the component itself.

Autodocs

Storybook can generate a documentation page from your stories and their args. With autodocs enabled, each component gets a page listing its props, controls and every story, which stays in sync automatically because it is generated from the same source. You can enrich it with a description, usage notes and code examples using the parameters.docs API.

Interaction testing

A story can define a play function that runs after the component renders. It uses the same queries and events as Testing Library, so the mental model transfers directly.

// Search.stories.jsx
import { expect, userEvent, within } from "@storybook/test";

export const TypesAndSubmits = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);

    await userEvent.type(canvas.getByLabelText("Search"), "vitest");
    await userEvent.click(canvas.getByRole("button", { name: "Go" }));

    await expect(canvas.getByText("3 results")).toBeVisible();
  },
};

Because the test runs inside Storybook, you can watch it step by step, which makes debugging far easier than a failing CI run. The same stories can be executed headlessly in CI with the Storybook test runner.

Accessibility and visual testing

The accessibility addon runs axe on every story and reports violations in the addon panel, grouped by impact. It catches missing labels, poor contrast, invalid ARIA and similar issues while you build, which is far cheaper than fixing them later.

Visual regression tools capture a screenshot per story and compare it against a baseline. Because every state is already a story, you get broad visual coverage with little extra effort. A changed screenshot flags an unintended visual change for review.

Where Storybook fits

Storybook sits between unit tests and end-to-end tests. It is not a replacement for either. Use it to develop and document components in isolation, to test interactive states with play functions, and to audit accessibility and visuals. Keep fast logic tests in Vitest, component behaviour in Testing Library, and full journeys in Playwright.

Best practices

  • Write one story per meaningful state, including loading, empty and error.
  • Prefer args over inline markup so controls and docs stay accurate.
  • Colocate stories with their components and name them by purpose.
  • Use decorators for providers, themes and layout instead of duplicating wrappers.
  • Add play functions for interactions and run them in CI.
  • Enable the accessibility addon and fix violations as they appear.
  • Publish the built Storybook as living documentation.

Common mistakes

  • Only writing a “Default” story and missing real states.
  • Hardcoding props in render and losing controls and documentation.
  • Treating Storybook as a replacement for unit or end-to-end tests.
  • Letting stories drift from the component’s actual props.
  • Ignoring accessibility warnings until they accumulate.
  • Duplicating provider setup in every story instead of using a decorator.

Where to go next

Storybook turns components into a documented, testable catalogue. Pair it with Testing Library for the queries inside play functions, Vitest for logic tests and Playwright for end-to-end flows. Then pick your most reused component and give it stories for every state it can be in.

Defining story inputs

Args drive controls and documentation. Hardcoded JSX duplicates markup and cannot be tweaked live.

Prefer
export const Large = {
  args: {
    size: "large",
    children: "Save",
  },
};
Avoid
export const Large = {
  render: () => (
    <Button size="large">
      Save
    </Button>
  ),
};

Covering states

One story per meaningful state documents the component and gives tests something to run against.

Prefer
export const Loading = {
  args: { state: "loading" },
};
export const Empty = {
  args: { state: "empty" },
};
export const Error = {
  args: { state: "error" },
};
Avoid
export const Default = {
  args: { state: "ready" },
};
// every other state lives
// only in the real app

FAQ

Frequently asked questions

Keep learning

Related topics from the roadmap.

$ start learning

Ready to start learning Storybook?

Our interactive tutorial walks you through Storybook step by step — with quizzes and real code you can run in the browser.