You build a Button component inside a page with 50 other components. To test different states — loading, disabled, error — you hack props, reload the page, and squint at the result. QA finds edge cases you missed because you never tested the empty state.
What if every component had its own interactive playground where you could test every variant, every edge case, in isolation?
That's Storybook.
Quick Start
npx storybook@latest init
npm run storybook
Opens a browser at localhost:6006 with your component library.
Writing Stories
// 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"], // Auto-generates documentation
argTypes: {
variant: { control: "select", options: ["primary", "secondary", "ghost"] },
size: { control: "radio", options: ["sm", "md", "lg"] },
disabled: { control: "boolean" },
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: { children: "Click Me", variant: "primary" },
};
export const Secondary: Story = {
args: { children: "Cancel", variant: "secondary" },
};
export const Loading: Story = {
args: { children: "Saving...", variant: "primary", disabled: true },
};
export const WithIcon: Story = {
args: { children: "Download", variant: "primary" },
render: (args) => (
<Button {...args}>
<DownloadIcon /> {args.children}
</Button>
),
};
Storybook 8 — What's New
- Visual tests — screenshot every story, catch visual regressions
- Component testing — play functions test interactions within stories
- Autodocs — generate documentation from your components automatically
- React Server Components — render RSC in Storybook
- Performance — 2x faster startup, 4x faster builds
Interaction Testing
export const FormSubmission: Story = {
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
await step("Fill out form", async () => {
await userEvent.type(canvas.getByLabelText("Name"), "Aleksej");
await userEvent.type(canvas.getByLabelText("Email"), "dev@test.com");
});
await step("Submit and verify", async () => {
await userEvent.click(canvas.getByRole("button", { name: "Submit" }));
await expect(canvas.getByText("Success!")).toBeInTheDocument();
});
},
};
Tests run IN the story. You see the interaction play out visually and can debug with browser DevTools.
When to Choose Storybook
Choose Storybook when:
- You build a component library or design system
- Multiple developers work on UI components
- You need living documentation for your components
- Visual regression testing matters
Skip Storybook when:
- Small app with few reusable components
- Server-rendered pages without client components
- Team doesn't maintain shared UI components
The Bottom Line
Storybook turns component development from guesswork to precision. Every state, every variant, every edge case — visible, testable, documented.
Start here: storybook.js.org
Need custom data extraction, scraping, or automation? I build tools that collect and process data at scale — 78 actors on Apify Store and 265+ open-source repos. Email me: Spinov001@gmail.com | My Apify Actors
Top comments (0)