Документирование React-компонентов — JSDoc, Storybook и README

19 июня 2026
Автор

Олег Марков

Введение

Документирование компонентов — это инвестиция, которая окупается многократно. Хорошо задокументированный компонент используют правильно, не дублируют и не боятся изменять. Плохо задокументированный — копируют с ошибками, используют не по назначению и обходят стороной.

В React-экосистеме сложилось три уровня документации: JSDoc-аннотации прямо в коде, живые примеры в Storybook и README-файлы для компонентных библиотек. Рассмотрим каждый уровень подробно.

JSDoc — документация прямо в коде

JSDoc — стандарт документирования JavaScript/TypeScript, который понимают все современные IDE. Правильно оформленный JSDoc превращает подсказки редактора в полноценную документацию.

Документирование пропсов через интерфейс

Самый эффективный способ документировать пропсы в TypeScript — добавить JSDoc-комментарии к полям интерфейса:

interface DataTableProps<T extends Record<string, unknown>> {
  /** Массив данных для отображения в таблице */
  data: T[];

  /**
   * Конфигурация колонок таблицы.
   * Каждый объект описывает один столбец: ключ данных, заголовок и рендерер.
   */
  columns: ColumnDef<T>[];

  /**
   * Количество строк на странице.
   * @default 10
   * @minimum 5
   * @maximum 100
   */
  pageSize?: number;

  /**
   * Включает возможность сортировки по клику на заголовок колонки.
   * @default false
   */
  sortable?: boolean;

  /**
   * Включает фильтрацию строк по введённому тексту.
   * Поиск происходит по всем видимым колонкам.
   * @default false
   */
  filterable?: boolean;

  /**
   * Колбэк, вызываемый при клике на строку таблицы.
   * Получает элемент данных и его индекс в массиве.
   *
   * @param row - Элемент данных из массива data
   * @param index - Индекс элемента в массиве
   */
  onRowClick?: (row: T, index: number) => void;

  /**
   * Компонент-заглушка для пустого состояния.
   * Отображается когда data.length === 0.
   * По умолчанию показывает стандартное сообщение «Нет данных».
   */
  emptyState?: React.ReactNode;
}

Документирование компонента

/**
 * Таблица данных с поддержкой сортировки, фильтрации и пагинации.
 *
 * Компонент типизирован дженериком T — тип данных выводится автоматически
 * из переданного массива `data`.
 *
 * @template T - Тип элемента данных. Должен быть объектом.
 *
 * @example Базовое использование
 * 

tsx

  • const columns: ColumnDef[] = [
  • { key: 'name', header: 'Имя' },
  • { key: 'email', header: 'Email' },
  • ]; *
  • ``` *
  • @example С сортировкой и пагинацией
  • ```tsx
  • <DataTable
  • data={products}
  • columns={productColumns}
  • sortable
  • filterable
  • pageSize={25}
  • onRowClick={(product) => navigate(/products/${product.id})}
  • />
  • ``` *
  • @see {@link https://company-storybook.example.com/?path=/docs/data-table} Storybook */ function DataTable({ data, columns, pageSize = 10, sortable = false, filterable = false, onRowClick, emptyState, }: DataTableProps) { // Реализация компонента return ...
    ; } ```

Документирование хуков

Хуки — особенно важны для документирования, так как их поведение не всегда очевидно:

/**
 * Хук для управления асинхронными операциями с отслеживанием состояния.
 *
 * @template T - Тип возвращаемых данных
 * @template E - Тип ошибки (по умолчанию Error)
 *
 * @param asyncFunction - Асинхронная функция для выполнения
 * @param options - Дополнительные параметры
 * @param options.immediate - Выполнить сразу при монтировании компонента
 * @param options.onSuccess - Колбэк при успешном выполнении
 * @param options.onError - Колбэк при ошибке
 *
 * @returns Объект с состоянием и функцией запуска
 *
 * @example
 * 

tsx

  • function UserProfile({ userId }: { userId: string }) {
  • const { data, isLoading, error, execute } = useAsync(
  • () => api.fetchUser(userId),
  • { immediate: true }
  • ); *
  • if (isLoading) return ;
  • if (error) return ;
  • return ;
  • }
  • */
    function useAsync<T, E = Error>(
    asyncFunction: () => Promise<T>,
    options: {
     immediate?: boolean;
     onSuccess?: (data: T) => void;
     onError?: (error: E) => void;
    } = {}
    ): {
    data: T | null;
    isLoading: boolean;
    error: E | null;
    execute: () => Promise<void>;
    } {
    const [state, setState] = useState<{
     data: T | null;
     isLoading: boolean;
     error: E | null;
    }>({ data: null, isLoading: false, error: null });
    
    const execute = useCallback(async () => {
     setState(prev => ({ ...prev, isLoading: true, error: null }));
     try {
       const result = await asyncFunction();
       setState({ data: result, isLoading: false, error: null });
       options.onSuccess?.(result);
     } catch (err) {
       setState({ data: null, isLoading: false, error: err as E });
       options.onError?.(err as E);
     }
    }, [asyncFunction]);
    
    useEffect(() => {
     if (options.immediate) execute();
    }, []);
    
    return { ...state, execute };
    }
    

Storybook — живая документация

Storybook — стандарт для документирования UI-компонентов в React. Он создаёт изолированную среду, где каждый компонент можно увидеть и потрогать с разными пропсами.

Установка и настройка

# Установка в существующий проект
npx storybook@latest init

# Storybook запускается на localhost:6006
npm run storybook

Написание историй (Stories) — CSF 3

Component Story Format 3 (CSF3) — текущий стандарт написания историй:

// 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'],
  // Параметры на уровне всех историй
  parameters: {
    layout: 'centered',
    docs: {
      description: {
        component: 'Основная кнопка интерфейса. Поддерживает 4 варианта и 3 размера.',
      },
    },
  },
  // Дефолтные аргументы для всех историй
  args: {
    onClick: () => console.log('Clicked!'),
  },
};

export default meta;
type Story = StoryObj<typeof Button>;

// История: базовое состояние
export const Default: Story = {
  args: {
    label: 'Нажми меня',
    variant: 'primary',
  },
};

// История: с конкретными пропсами
export const Secondary: Story = {
  args: {
    label: 'Дополнительное действие',
    variant: 'secondary',
  },
};

// История: состояние загрузки
export const Loading: Story = {
  args: {
    label: 'Сохранение...',
    variant: 'primary',
    isLoading: true,
  },
};

// История: заблокированная
export const Disabled: Story = {
  args: {
    label: 'Недоступно',
    isDisabled: true,
  },
};

// История: все размеры
export const AllSizes: Story = {
  render: () => (
    <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
      <Button label="Маленькая" size="small" />
      <Button label="Средняя" size="medium" />
      <Button label="Большая" size="large" />
    </div>
  ),
  parameters: {
    docs: {
      description: {
        story: 'Компонент доступен в трёх размерах: small, medium и large.',
      },
    },
  },
};

Документирование через autodocs

Storybook с тегом autodocs автоматически генерирует таблицу пропсов из TypeScript-типов и JSDoc-комментариев:

// Чтобы autodocs работал корректно — важно:
// 1. TypeScript-типы должны быть явными (не any)
// 2. JSDoc на интерфейсе пропсов
// 3. Значения по умолчанию в деструктуризации

interface CardProps {
  /** Заголовок карточки */
  title: string;

  /** Описание (опциональное) */
  description?: string;

  /**
   * Визуальное выделение карточки.
   * @default false
   */
  highlighted?: boolean;

  /** Дочерние элементы */
  children: React.ReactNode;
}

function Card({ title, description, highlighted = false, children }: CardProps) {
  return (
    <div className={highlighted ? 'card card--highlighted' : 'card'}>
      <h3>{title}</h3>
      {description && <p>{description}</p>}
      {children}
    </div>
  );
}

Декораторы для контекста

Многим компонентам нужен контекст (тема, роутер, i18n). В Storybook это решается через декораторы:

// .storybook/preview.tsx
import { MemoryRouter } from 'react-router-dom';
import { ThemeProvider } from '../src/contexts/ThemeContext';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient({
  defaultOptions: { queries: { retry: false } }
});

export const decorators = [
  (Story) => (
    <QueryClientProvider client={queryClient}>
      <ThemeProvider>
        <MemoryRouter>
          <Story />
        </MemoryRouter>
      </ThemeProvider>
    </QueryClientProvider>
  ),
];

// Или на уровне конкретной истории
export const WithAuth: Story = {
  decorators: [
    (Story) => (
      <AuthProvider user={mockUser}>
        <Story />
      </AuthProvider>
    ),
  ],
  args: {
    // ...
  },
};

Interaction Tests в Storybook

Storybook позволяет писать автоматические тесты взаимодействия прямо в историях:

import { within, userEvent, expect } from '@storybook/test';

export const FormSubmission: Story = {
  args: {
    onSubmit: fn(),
  },
  play: async ({ canvasElement, args }) => {
    const canvas = within(canvasElement);

    // Находим поля ввода
    const emailInput = canvas.getByLabelText('Email');
    const passwordInput = canvas.getByLabelText('Пароль');
    const submitButton = canvas.getByRole('button', { name: 'Войти' });

    // Заполняем форму
    await userEvent.type(emailInput, 'user@example.com');
    await userEvent.type(passwordInput, 'password123');

    // Отправляем
    await userEvent.click(submitButton);

    // Проверяем, что колбэк вызван с правильными данными
    await expect(args.onSubmit).toHaveBeenCalledWith({
      email: 'user@example.com',
      password: 'password123',
    });
  },
};

README для компонентных библиотек

Когда компонент или библиотека компонентов публикуется как пакет — README становится первым, что видит разработчик.

Структура хорошего README

# ComponentName

Краткое описание в одном предложении — что делает компонент и для чего.

## Установка

bash npm install @company/components


## Быстрый старт

tsx import { ComponentName } from '@company/components';

function App() { return ; }


## Пропсы

| Пропс | Тип | По умолчанию | Описание |
|-------|-----|--------------|----------|
| `prop1` | `string` | — | Обязательный пропс |
| `prop2` | `number` | `10` | Опциональный пропс |
| `onAction` | `() => void` | — | Вызывается при действии |

## Примеры

### Базовое использование

tsx


### С дополнительными параметрами

tsx console.log('действие')} />


## Доступность

- Компонент поддерживает навигацию с клавиатуры
- Соответствует WCAG 2.1 уровня AA
- Использует семантические HTML-элементы

## Связанные компоненты

- [RelatedComponent](./RelatedComponent/README.md)

Документирование сложных паттернов использования

// Плохо: нет примера для сложного случая
/**
 * Компонент SelectField с поиском и мультивыбором.
 */

// Хорошо: есть примеры для всех ключевых случаев использования
/**
 * Компонент SelectField с поиском и мультивыбором.
 *
 * @example Одиночный выбор
 * 

tsx

  • const [value, setValue] = useState(''); *
  • <SelectField
  • options={countries}
  • value={value}
  • onChange={setValue}
  • placeholder="Выберите страну"
  • />
  • ``` *
  • @example Мультивыбор
  • ```tsx
  • const [values, setValues] = useState<string[]>([]); *
  • <SelectField
  • multiple
  • options={tags}
  • value={values}
  • onChange={setValues}
  • placeholder="Выберите теги"
  • />
  • ``` *
  • @example Асинхронная загрузка опций
  • ```tsx
  • <SelectField
  • options={[]}
  • isLoading={isLoadingOptions}
  • onSearchChange={async (query) => {
  • const results = await searchUsers(query);
  • setOptions(results);
  • }}
  • />
  • */

Автогенерация документации

TypeDoc

TypeDoc генерирует HTML-документацию из TypeScript-кода и JSDoc:

npm install typedoc --save-dev

# typedoc.json
{
  "entryPoints": ["./src/index.ts"],
  "out": "docs",
  "plugin": ["typedoc-plugin-markdown"],
  "theme": "markdown",
  "readme": "README.md"
}

# Генерация
npx typedoc

react-docgen

react-docgen — инструмент для извлечения информации о пропсах из JSX-компонентов:

npm install react-docgen --save-dev

# Извлечение документации из компонента
npx react-docgen src/components/Button/Button.tsx --pretty

Вывод:

{
  "description": "Основная кнопка интерфейса",
  "props": {
    "label": {
      "type": { "name": "string" },
      "required": true,
      "description": "Текст кнопки"
    },
    "variant": {
      "type": { "name": "enum", "value": ["primary", "secondary", "danger"] },
      "required": false,
      "defaultValue": { "value": "primary" },
      "description": "Визуальный стиль кнопки"
    }
  }
}

Чеклист документирования компонента

Перед тем как считать компонент готовым — пройдитесь по чеклисту:

  • JSDoc на интерфейсе пропсов — каждый пропс описан
  • JSDoc на самом компоненте — краткое описание назначения
  • Значения по умолчанию указаны в JSDoc (@default)
  • Хотя бы один @example в JSDoc
  • История в Storybook для каждого важного состояния
  • Storybook autodocs генерирует корректную таблицу пропсов
  • README обновлён (если это пакет)
  • Нетривиальные пропсы имеют подробное описание

Связанные темы

Стрелочка влевоИменование компонентов в React — соглашения и лучшие практикиКомментирование кода в React — когда и как писать комментарииСтрелочка вправо

Постройте личный план изучения React до уровня Middle — бесплатно!

React — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по React

Uncontrolled Components: когда DOM управляет даннымиБезопасность в React: защита от XSS, CSRF и утечек данныхRender Props: гибкое управление рендерингом в ReactРефакторинг React-кода: техники и лучшие практикиПрофилирование React: как найти и устранить узкие местаЧастичное применение: как создавать компоненты без лишнего кодаИменование компонентов в React: соглашения и лучшие практикиЛенивая загрузка: как ускорить React-приложение в разыHOC в React: мастерство композиции компонентовuseMemo: как спасти производительность от тяжелых вычисленийError Boundaries: создаем надежные React-приложенияКонтролируемые компоненты в React: полный контроль над формамиCompound Components в React: создаем гибкие компоненты с мощным APIДокументирование компонентов в React: Storybook, JSDoc и READMEКомпозиция компонентов в React: строим гибкие интерфейсыКомментирование кода в React: когда и как писать комментарииCode Splitting в React: как уменьшить бандл и ускорить загрузку приложенияАсинхронные компоненты в React: новый стандарт работы с даннымиДоступность (a11y) в React: ARIA, семантика и клавиатурная навигация
Zustand — управление состоянием в ReactZod - валидация с TypeScriptYup - валидация схемXState - конечные автоматыТемизация в ReactТестирование хуковTailwind CSS с ReactSWR - библиотека для запросовStyled Components — стилизация через JSStorybook - документация компонентовSnapshots тестированиеRTK Query - работа с APIRedux Toolkit - современный ReduxRecoil — библиотека управления состоянием от FacebookВиртуализация списков с react-window: как отображать тысячи элементов без лаговReact Toastify - уведомления в ReactReact Testing LibraryСоздание таблиц в React гайд по react-tableReact Spring - анимацииРабота с формами и селектами в ReactReact Query (TanStack Query) - работа с серверомПлагины в React что это и как их использоватьReact PDF - работа с PDF файламиОбзор популярных библиотек для ReactReact Icons - библиотека иконок для ReactReact Hook Form — валидация форм в ReactReact Dropzone — загрузка файловПодключение Bootstrap к React-приложениюReact Beautiful DnD - перетаскивание элементовАнимация при монтировании компонентов в ReactМокирование APIMobX — реактивное управление состоянием в ReactМикрофронтенды с React (micro-frontends)Загрузка и индикаторыАнимация списков в ReactJotai - атомарное состояниеБесконечная прокруткаFramer Motion - библиотека анимацийEmotion — библиотека CSS-in-JSДинамические стили в ReactE2E тестирование с CypressCSSTransition - переходыCSS-in-JS — плюсы и минусыКонтекст vs Redux — когда что использоватьИспользование Chart.js в ReactAxios с ReactТестирование асинхронных компонентовОбработка ошибок API
useState в React что это и как использоватьuseTransition - плавные переходы между состояниямиuseSyncExternalStore — работа с внешними сторамиuseRef в React — создание ссылок на DOM и значенияuseOptimistic — оптимистичные обновления UIuseLayoutEffect в React — эффект до отрисовкиuseInsertionEffect — внедрение стилей до мутаций DOMuseImperativeHandle в React — настройка ref дочернего компонентаuseId — генерация уникальных идентификаторовuseFormStatus - отслеживание статуса отправки формыuseDeferredValue — отложенное обновление состоянияuseDebugValue — отладка кастомных хуковuseCallback в React — мемоизация функцийuseReducer — альтернатива useState для сложной логикиuseMemo в React: как и когда оптимизировать тяжелые вычисленияuseEffect в React что это и как использоватьuseContext — работа с контекстом в ReactuseCallback в React — мемоизация функций и оптимизация ре-рендеровuseActionState в React 19Оптимизация рендеринга в React: от теории к глубокой практикеЧто такое useRef и как его применять в ReactКак и зачем использовать React HooksУправление состоянием в React через ContextКак предотвратить лишние ре-рендеры в React: полное руководствоuseMemo vs useCallback: подробное руководство по мемоизации в ReactПравила хуков — правила использованияuseEffect vs useLayoutEffect: в чём разница и какой хук выбрать?Кастомные хуки в React — создание собственных хуковuseState продвинутое использование в React
Transition API — плавные обновления интерфейса в ReactReact Suspense — приостановка рендераStrictMode в React — как находить ошибки на этапе разработкиСерверные компоненты React (RSC) — подробный разбор и практикаКак работает рендеринг в ReactЧто такое props в React и как их правильно использоватьКак работает JSX связка React и HTMLЧто такое React.js и как его использоватьКак использовать элементы в ReactКак использовать React DOM в проектеЧто такое компоненты в React и как их применятьРабота с children в ReactПорталы в React: рендер компонентов вне иерархии DOMFragment в React: группировка элементов без лишних узлов DOMCSS Modules в ReactConcurrent Mode — конкурентный режим в React
Открыть базу знаний

Лучшие курсы по теме

изображение курса

React и Redux Toolkit

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий