Storybook is the UI workshop for building components in isolation. Its API lets you document, test, and showcase every component state.
Write a Story
import type { Meta, StoryObj } from "@storybook/react";
import { Button } from "./Button";
const meta: Meta<typeof Button> = {
title: "Components/Button",
component: Button,
tags: ["autodocs"],
argTypes: {
variant: { control: "select", options: ["primary", "secondary", "danger"] },
size: { control: "radio", options: ["sm", "md", "lg"] },
disabled: { control: "boolean" },
onClick: { action: "clicked" },
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: { variant: "primary", children: "Click me", size: "md" },
};
export const Secondary: Story = {
args: { variant: "secondary", children: "Cancel" },
};
export const Disabled: Story = {
args: { variant: "primary", children: "Disabled", disabled: true },
};
export const AllVariants: Story = {
render: () => (
<div style={{ display: "flex", gap: 8 }}>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Danger</Button>
</div>
),
};
Interaction Tests
import { within, userEvent, expect } from "@storybook/test";
export const WithInteraction: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByRole("button");
await expect(button).toBeEnabled();
await userEvent.click(button);
await expect(canvas.getByText("Clicked!")).toBeInTheDocument();
},
};
export const FormTest: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.type(canvas.getByLabelText("Email"), "test@example.com");
await userEvent.type(canvas.getByLabelText("Password"), "password123");
await userEvent.click(canvas.getByRole("button", { name: "Submit" }));
await expect(canvas.getByText("Success!")).toBeInTheDocument();
},
};
Decorators: Wrap Components
const meta: Meta<typeof Card> = {
component: Card,
decorators: [
(Story) => (
<div style={{ padding: "3rem", background: "#f5f5f5" }}>
<Story />
</div>
),
// Theme provider
(Story) => (
<ThemeProvider theme={darkTheme}>
<Story />
</ThemeProvider>
),
],
};
Loaders: Async Data
export const WithData: Story = {
loaders: [
async () => ({
products: await fetch("/api/products").then(r => r.json()),
}),
],
render: (args, { loaded: { products } }) => (
<ProductGrid products={products} />
),
};
Visual Testing: Chromatic
npx chromatic --project-token=YOUR_TOKEN
# Captures screenshots of every story
# Detects visual regressions automatically
Document your data components? My Apify tools provide realistic test data for Storybook.
Custom component library? Email spinov001@gmail.com
Top comments (0)