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
renderand 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.