Storybook — стандарт для разработки UI-компонентов в изоляции. Каждый компонент в собственной «витрине» со всеми вариантами, состояниями, props. Дизайнер видит, что получилось; разработчик быстро тестирует крайние случаи; тестировщик гоняет визуальные регрессы. Звучит идеально, но в половине проектов Storybook ставят, два раза заходят и забывают. Разбираем, как сделать его реально полезным.
Когда Storybook нужен
| Ситуация | Нужен Storybook |
|---|---|
| Дизайн-система на 30+ компонентов | да, обязательно |
| Команда от 4-5 фронтендеров | да |
| Несколько проектов, переиспользующих UI-компоненты | да |
| Лендинг на 5 страниц, один разработчик | нет |
| MVP с дедлайном 2 месяца | нет, сначала продукт |
| SaaS-продукт от 6 месяцев разработки | да, окупится |
Правило: Storybook окупается, когда стоимость поддержки вашего UI-кода превышает 20% времени команды или когда дизайнер не может найти, как выглядит компонент в варианте X.
Установка в проект на Next.js 15
npx storybook@latest init
Wizard определит фреймворк (Next.js + React 19) и сгенерирует:
.storybook/main.ts— конфигурация.storybook/preview.tsx— глобальные параметры, декораторыsrc/stories/*.stories.tsx— примеры
Запуск: npm run storybook → открывается на :6006.
Структура story
// src/components/Button/Button.stories.tsx
import type { Meta, StoryObj } from "@storybook/react";
import { Button } from "./Button";
const meta: Meta<typeof Button> = {
title: "UI/Button",
component: Button,
tags: ["autodocs"],
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: { variant: "primary", children: "Click me" },
};
export const Loading: Story = {
args: { variant: "primary", loading: true, children: "Loading..." },
};
export const Disabled: Story = {
args: { variant: "primary", disabled: true, children: "Disabled" },
};
Каждый export — отдельная story. argTypes рисует панель управления props в правом сайдбаре — дизайнер сам играется без разработчика.
Tailwind в Storybook
Подключите глобальный CSS в preview:
// .storybook/preview.tsx
import "../src/app/globals.css";
import type { Preview } from "@storybook/react";
const preview: Preview = {
parameters: {
backgrounds: {
default: "light",
values: [
{ name: "light", value: "#ffffff" },
{ name: "dark", value: "#0b0d12" },
],
},
},
};
export default preview;
Если есть тёмная тема — добавьте global toolbar:
export const globalTypes = {
theme: {
description: "Theme",
defaultValue: "light",
toolbar: {
title: "Theme",
icon: "circlehollow",
items: [
{ value: "light", title: "Light" },
{ value: "dark", title: "Dark" },
],
},
},
};
export const decorators = [
(Story, ctx) => {
document.documentElement.dataset.theme = ctx.globals.theme;
return <Story />;
},
];
Документация через MDX
Storybook 8 поддерживает MDX-стории — можно писать живую документацию с описаниями и примерами:
import { Meta, Story, Canvas } from "@storybook/blocks";
import * as ButtonStories from "./Button.stories";
<Meta of={ButtonStories} />
# Button
Универсальная кнопка дизайн-системы. Используется во всех CTA, формах, модалках.
## Варианты
<Canvas of={ButtonStories.Primary} />
<Canvas of={ButtonStories.Secondary} />
## Когда использовать
- **Primary** — главное действие на странице, не более одного.
- **Secondary** — альтернативное действие.
- **Ghost** — третичное, фон прозрачный.
## Размеры
| Размер | Применение |
|---|---|
| sm | внутри карточек, в табах |
| md | формы, основные CTA |
| lg | hero-секции |
Получаются вкладки «Docs» и «Story» — одна для дизайнера, другая для разработчика.
Visual regression — Chromatic
Самая полезная фича. Каждый PR прогоняется через Chromatic, который делает скриншоты всех stories и сравнивает с предыдущими. Если какой-то компонент изменился визуально — PR блокируется до подтверждения дизайнера.
npx chromatic --project-token=...
Бесплатно: 5000 снимков в месяц, для команды до 5 человек хватает. Платно — от $149/мес.
Альтернативы self-hosted:
- Lost Pixel — open source, можно бесплатно self-hosted.
- Reg-suit — старая, но рабочая.
- Playwright + own pipeline — самописный visual diff.
Тесты внутри Storybook
С @storybook/test можно писать interaction tests:
import { within, userEvent, expect } from "@storybook/test";
export const SubmitFlow: Story = {
args: {},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.type(canvas.getByLabelText("Email"), "test@example.ru");
await userEvent.click(canvas.getByRole("button", { name: /отправить/i }));
await expect(canvas.getByText(/успешно/i)).toBeInTheDocument();
},
};
Тесты гоняются прямо в Storybook UI и в CI через test-runner. Покрывают ровно то, что видит пользователь — никаких mock'ов и фиктивных деревьев.
Интеграция с Figma
Аддон @storybook/addon-designs встраивает Figma-фрейм рядом со story:
const meta: Meta<typeof Button> = {
// ...
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/file/abc123/Design-System?node-id=42",
},
},
};
Дизайнер обновил Figma — разработчик в Storybook сразу видит свежую версию рядом со своей реализацией. Очень удобно при ревью.
Деплой
Сборка статики: npm run build-storybook. Кладётся в storybook-static/ — обычная статика, заливается куда угодно: Vercel, Netlify, Yandex Object Storage с раздачей через CDN, GitHub Pages.
Для команды удобно — Storybook на собственном поддомене вроде ui.example.ru. Дизайнер бросает ссылку «глянь, как кнопка выглядит» — без настройки проекта, без npm install.
Антипаттерны
- Stories на каждый внутренний компонент. Достаточно атомов и составных. Story для одноразовой формы — мусор.
- Stories без actions и edge cases. «Default» без вариантов — бесполезная страница.
- Storybook в репо, но никто не открывает. Если за неделю никто не зашёл — выкиньте, не мучайте.
- Истории дублируют тесты. Если уже есть Jest-снапшоты компонента, смысла в Storybook-снимках нет — выберите одно.
- Stories с моками внешних API. Компонент должен принимать данные через props, а не дёргать fetch — иначе story работает только с интернетом.
Storybook 9 на горизонте
К середине 2026 ожидается Storybook 9 с упрощённой архитектурой, более быстрым стартом (Vite-only), встроенным a11y-аудитом. Если стартуете сейчас — берите 8 и не переживайте, миграция на 9 будет несложной.
Итого
Storybook — инструмент для команд. Для одиночки или короткого проекта — оверкилл. Для дизайн-системы и продукта от полугода — экономит часы дискуссий «а как должна выглядеть кнопка в loading state на dark теме». Главное — поддерживать актуальность: устаревший Storybook хуже его отсутствия.
Частые вопросы
Сколько времени уходит на поддержку Storybook?
На старте — 5-10% времени фронтенд-разработки на компонент. Установился ритм: новый компонент → story в той же таске. Спустя месяц-два уходит 1-2% — большинство стори меняется реже, чем сами компоненты. Если вы тратите больше — что-то делаете не так, скорее всего пишете слишком детальные истории.
Storybook или Ladle?
Ladle — быстрый минималистичный аналог, на голом Vite. Холодный старт за 1-2 секунды против 10-30 у Storybook. Если нужны только простые stories без MDX, аддонов и Chromatic — Ladle. Если нужна полноценная дизайн-система с документацией, тестами, интеграциями — Storybook.
Можно ли публиковать Storybook как публичную документацию для клиентов?
Можно, но это не лучший формат — Storybook оптимизирован под разработчиков. Для публичной документации лучше Docusaurus, Mintlify, Nextra. А Storybook оставить как «техническую витрину» для команды и подрядчиков. Часто делают и то, и другое: Storybook закрыт паролем, публичные доки — отдельный сайт, генерируются из тех же MDX-файлов.
Как тестировать Server Components в Storybook?
На момент 2026 — никак напрямую. Storybook требует клиентский рантайм, RSC так не работают. Обходное решение: оборачивать серверный компонент в клиентский шаблон, где данные передаются через props (то есть тестировать «презентационную часть»). Чистые серверные компоненты с async/await тестируйте через Playwright или Jest с моком БД.
Сколько стоит Chromatic для команды из 5 человек?
Бесплатно до 5000 снимков в месяц — этого хватает для маленького проекта (50-100 компонентов × 1-2 PR в день). Если упираетесь — $149/мес за 35 000 снимков. Для крупной команды разумно поднять Lost Pixel или свой visual diff на Playwright — экономия в разы.
Как замотивировать команду писать stories?
Ввести в Definition of Done: «компонент готов = есть story и она проходит CI». Без этого 80% разработчиков забывают через две недели. Plus-сторонний эффект: дизайнер регулярно заходит и комментирует — разработчики видят отдачу и не воспринимают это как формальность.
Storybook замедляет билд основного проекта?
Нет, это отдельная сборка с собственным процессом. На основной next build не влияет. Единственное — нужно следить, что сами stories не импортируют что-то лишнее в основной бандл (например, если Storybook-only утилиту случайно импортнули в продакшн-компонент, она попадёт в бандл).