Legan Studio
Все статьи
~ 5 мин чтения

Storybook для дизайн-системы

Storybook 8 для React-компонентов: настройка, MDX-доки, визуальные тесты, Chromatic, интеграция с Figma и когда он реально нужен.

  • веб
  • разработка
  • дизайн

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.

Антипаттерны

  1. Stories на каждый внутренний компонент. Достаточно атомов и составных. Story для одноразовой формы — мусор.
  2. Stories без actions и edge cases. «Default» без вариантов — бесполезная страница.
  3. Storybook в репо, но никто не открывает. Если за неделю никто не зашёл — выкиньте, не мучайте.
  4. Истории дублируют тесты. Если уже есть Jest-снапшоты компонента, смысла в Storybook-снимках нет — выберите одно.
  5. 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 утилиту случайно импортнули в продакшн-компонент, она попадёт в бандл).