Storybook is the visual source of truth for components. Each story demonstrates a real state; Docs explains the contract — not a parallel PDF.
Complements design-system and react-component-engineering. Visual consistency across apps: better-web-interface.
Stack and scope
| Item | Role |
|---|---|
| Storybook 8+ | Stories runtime (preserve the project's major) |
| CSF 3 | meta + named exports |
| Docs / Autodocs | Documentation alongside the canvas |
| Controls / Args | Interactive props |
| Addons | a11y, viewport, themes — the project's own |
Do not migrate major versions or switch Vite/Webpack without a request. Use the setup already in the repo.
Non-negotiable rules
- One stories file per public design system component (or per documented folder).
- Stories cover risk states: default, disabled, loading, error, empty, variants.
- Typed args aligned with the real props — no fake props just for Storybook.
- Docs describes when to use / when not to use, not just the API.
- Theme/router/i18n decorators in the preview — do not duplicate boilerplate in each story.
- Do not document internal components that are not exported as public API.
CSF 3
import type { Meta, StoryObj } from "@storybook/react";
import { Button } from "./Button";
const meta = {
title: "Primitives/Button",
component: Button,
tags: ["autodocs"],
args: { children: "Continue", variant: "primary" },
argTypes: {
variant: { control: "select", options: ["primary", "secondary", "ghost"] },
},
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Primary: Story = {};
export const Secondary: Story = {
args: { variant: "secondary" },
};
export const Disabled: Story = {
args: { disabled: true },
};
export const Loading: Story = {
args: { loading: true },
};
title: stable hierarchy (Primitives/,Patterns/,Features/).- Prefer
argsover duplicated JSX; userenderonly when the composition requires it. satisfies Meta<typeof Component>for typechecking the args.
What to document in Docs
For each public component:
- Purpose — one sentence.
- When to use / avoid — clear boundaries.
- Variants and anatomy — primary/secondary, slots.
- Accessibility — keyboard, roles, required labels.
- Tokens — which color/spacing/type variables it consumes.
- Do / Don't — 1–2 visual examples when the mistake is common.
MDX only when autodocs + description in the meta are not enough (brand guides, foundations overview).
Foundations in Storybook
Also document (as stories or overview MDX):
- Semantic colors / typography / spacing / radius / elevation
- Icons
- Breakpoints and grid
- Light/dark theme (theme toolbar)
This prevents the design system from existing “only in Figma”.
Addons and quality
- a11y: run in CI or as a local gate on UI PRs.
- viewport: critical stories on mobile + desktop.
- theme: light/dark toggle if the product has one.
- Avoid stories that depend on a real network; mock data at the edge.
- Chromatic/visual regression if the project already uses it — stable stories (no time/random).
Organization
src/components/Button/
Button.tsx
Button.stories.tsx
Button.mdx # só se necessário além de autodocs
- Place stories close to the code (
*.stories.tsx) unless a different monorepo convention applies. - Design system package: stories in the UI package; the app can have stories for composite patterns.
Anti-patterns
- A “KitchenSink” story with all props mixed together
- Outdated docs with props that don't exist
- Screenshots in the README instead of stories
- A story that mounts the entire app page unnecessarily
- Ignoring disabled/loading/error states
- Args that don't match the component's TypeScript API
Flow when documenting a component
- Confirm the public API and real variants in the code.
- Meta + autodocs + default args.
- Stories per relevant state/variant.
- Description / Do-Don't / a11y notes.
- Check theme, viewport, and the a11y addon.
- If it's a new foundation, link it in the design system overview.
Completion criteria
- Typed CSF stories for the component touched
- Main states covered and navigable via controls
- Autodocs or MDX with purpose + usage limits
- Theme/a11y/viewport aligned with the project preview
- No obvious drift between TypeScript props and docs