В этой статье мы подробно разберем процесс настройки UI‑Kit на React — от установки зависимостей до сборки готового пакета. Мы настроим полный цикл разработки: сборку, тестирование, линтинг и документацию.
Если вы уже все это знаете и хотите сэкономить время и просто склонировать готовый шаблон, то эта ссылка для вас.
Также вы можете взять компоненты дизайн‑системы по ссылке.
? Начало работы: инициализация проекта
Шаг 1: Создаем проект и устанавливаем зависимости
bash
# Создаем директорию и инициализируем package.json
mkdir ui-kit
cd ui-kit
npm init -y
Здесь мы создаем основу нашего будущего UI‑Kit — папку проекта и файл package.json. Этот файл является сердцем любого Node.js проекта: в нем хранится информация о проекте, его зависимостях и скриптах. Ключ -y позволяет согласиться со значениями по умолчанию, чтобы быстро пройти инициализацию.
Шаг 2: Устанавливаем основные зависимости
bash
# React и TypeScript
npm install --save-dev react react-dom
npm install --save-peer react react-dom
npm install --save-dev typescript @types/react @types/react-dom
React и ReactDOM — это ядро, на котором будет построена наша библиотека. TypeScript добавляет статическую типизацию, что помогает предотвращать ошибки на этапе разработки и улучшает автодополнение. Мы устанавливаем React и как peerDependencies (чтобы избежать дублирования в финальном приложении), и как devDependencies (чтобы использовать их во время разработки и тестирования).
Шаг 3: Устанавливаем инструменты сборки
bash
# Rollup и плагины
npm install -D rollup
npm install -D @rollup/plugin-typescript
npm install -D @rollup/plugin-node-resolve
npm install -D @rollup/plugin-commonjs
npm install -D rollup-plugin-postcss
npm install -D postcss
Rollup — это современный сборщик модулей, идеально подходящий для библиотек. Мы настраиваем его с помощью плагинов:
@rollup/plugin-typescriptдля работы с TypeScript;@rollup/plugin-node-resolveчтобы Rollup мог находить модули вnode_modules;@rollup/plugin-commonjsдля преобразования модулей CommonJS в ES‑модули, которые понимает Rollup;rollup-plugin-postcssдля обработки CSS‑файлов, их минификации и извлечения в отдельный файл.
Шаг 4: Устанавливаем тестирование
bash
# Jest и Testing Library
npm install -D jest jest-environment-jsdom
npm install -D @testing-library/react @testing-library/jest-dom
npm install -D @testing-library/user-event @testing-library/dom
npm install -D @types/jest identity-obj-proxy
Jest — это мощный и популярный фреймворк для тестирования. Testing Library предоставляет набор утилит для тестирования React‑компонентов так, как это делают пользователи, фокусируясь на их доступности и поведении, а не на внутренней реализации. identity-obj-proxy помогает имитировать импорт CSS‑модулей в тестах.
Шаг 5: Устанавливаем Babel для транспиляции
bash
# Babel пресеты
npm install -D @babel/preset-env
npm install -D @babel/preset-react
npm install -D @babel/preset-typescript
Babel преобразует современный JavaScript и JSX/TSX‑код в версию, понятную старым браузерам и средам (например, Jest). Пресеты — это предустановленные наборы правил для преобразования определенных синтаксических конструкций (ES6+, React, TypeScript).
Шаг 6: Устанавливаем линтинг и форматирование
bash
# ESLint и Prettier
npm install -D eslint @eslint/js jiti
npm install -D typescript-eslint
npm install -D eslint-plugin-react
npm install -D prettier
npm install -D globals
ESLint и Prettier — незаменимые инструменты для поддержания качества и единообразия кода. ESLint находит и исправляет проблемные паттерны в коде, а Prettier автоматически форматирует код по заданным правилам, избавляя команду от споров о стиле.
Шаг 7: Устанавливаем Storybook для документации
bash
# Storybook
npm install -D storybook @storybook/react
npm install -D @storybook/react-vite
Storybook — это интерактивная среда для разработки и документирования компонентов в изоляции. Она позволяет просматривать компоненты в разных состояниях, писать для них документацию и тестировать их визуально.
⚙️ Настройка конфигурационных файлов
Шаг 8: Создаем структуру проекта
bash
# Создаем основную структуру
mkdir -p src/Button src/_internal/test src/types .storybook
Четкая структура папок — залог поддерживаемости кода. Мы заранее создаем папки для исходного кода (src), компонентов (src/Button), внутренних утилит (src/_internal), типов и конфигурации Storybook.
Шаг 9: Настраиваем TypeScript
Создаем tsconfig.json:
json
{
"compilerOptions": {
"target": "ES2022",
"lib": ["DOM", "ES2022"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"declaration": true,
"outDir": "lib",
"rootDir": "src",
"module": "ESNext",
"types": ["jest", "@testing-library/jest-dom"]
},
"include": ["src", "*.js", "*.ts"],
"exclude": ["lib", "node_modules", "**/*.stories.tsx", "**/*.test.tsx"]
}
Файл tsconfig.json сообщает TypeScript, как компилировать наш проект. Мы настраиваем его для работы с современным JavaScript (ES2022), строгой типизацией (strict: true), JSX и генерацией файлов с объявлениями типов (declaration: true), которые необходимы для использования нашей библиотеки в других TypeScript‑проектах.
Шаг 10: Настраиваем Rollup для сборки
Создаем rollup.config.js:
javascript
import commonjs from "@rollup/plugin-commonjs";
import resolve from "@rollup/plugin-node-resolve";
import typescript from "@rollup/plugin-typescript";
import postcss from "rollup-plugin-postcss";
const pkg = require("./package.json");
const external = [
...(pkg.dependencies ? Object.keys(pkg.dependencies) : []),
...(pkg.devDependencies ? Object.keys(pkg.devDependencies) : []),
...(pkg.peerDependencies ? Object.keys(pkg.peerDependencies) : []),
];
const baseOutput = {
dir: "lib",
sourcemap: true,
exports: "named",
};
const plugins = [
postcss({
modules: true,
extract: true,
minimize: true,
inject: false,
}),
resolve({
extensions: [".ts", ".tsx", ".js", ".jsx"],
}),
commonjs(),
typescript({
outDir: "lib",
declarationDir: "lib",
declaration: true,
rootDir: "src",
}),
];
export default [
{
input: ["src/index.ts"],
output: [
{
...baseOutput,
format: "esm",
},
{
...baseOutput,
format: "cjs",
entryFileNames: "[name].cjs",
},
],
external,
plugins,
},
];
Это основная конфигурация нашего сборщика. Мы указываем точку входа, настраиваем плагины для обработки разных типов файлов и задаем сборку в двух форматах — ESM (для современных сборщиков) и CommonJS (для Node.js и некоторых других окружений). Важно отметить внешние зависимости (external), чтобы они не попадали в бандл нашей библиотеки.
Шаг 11: Настраиваем Jest для тестирования
Создаем jest.config.json:
json
{
"clearMocks": true,
"logHeapUsage": true,
"passWithNoTests": true,
"testEnvironment": "jsdom",
"transform": {
"^.+\\.(ts|tsx|js|jsx)$": "babel-jest"
},
"transformIgnorePatterns": ["node_modules/(?!(.*\\.css$))"],
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json"],
"setupFilesAfterEnv": ["<rootDir>/src/_internal/test/setupTest.ts"],
"moduleNameMapper": {
"\\.(css|less|scss|sass)$": "identity-obj-proxy"
}
}
```
Конфигурация Jest подключает окружение для тестирования DOM (`jsdom`), настраивает Babel для транспиляции тестов, добавляет маппинг для CSS-модулей (чтобы Jest их понимал) и указывает файл с дополнительной настройкой тестового окружения.
Шаг 12: Настраиваем Babel
Создаем `.babelrc.json`:
```json
{
"presets": [
["@babel/preset-env", { "targets": { "node": "current" } }],
["@babel/preset-react", { "runtime": "automatic" }],
"@babel/preset-typescript"
]
}
Babel использует пресеты, которые мы установили ранее, чтобы преобразовывать наш код. Мы настраиваем его для поддержки последних версий JavaScript, React с новой JSX‑трансформацией (runtime: automatic) и TypeScript.
Шаг 13: Настраиваем ESLint
Создаем eslint.config.ts:
typescript
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import pluginReact from "eslint-plugin-react";
import { defineConfig } from "eslint/config";
export default defineConfig([
{
ignores: ["lib/**", "dist/**", "build/**"],
},
{
files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
plugins: { js },
extends: ["js/recommended"],
languageOptions: {
globals: {
...globals.browser,
...globals.node,
require: "readonly",
},
},
},
tseslint.configs.recommended,
{
...pluginReact.configs.flat.recommended,
settings: {
react: {
version: "detect",
},
},
},
{
files: ["**/*.config.js", "**/*.config.ts", "rollup.config.js"],
rules: {
"@typescript-eslint/no-require-imports": "off",
},
},
{
files: ["**/*.{jsx,tsx}"],
rules: {
"react/react-in-jsx-scope": "off",
},
},
]);
Новая плоская конфигурация ESLint позволяет гибко настраивать правила. Мы подключаем рекомендованные конфигурации для JavaScript, TypeScript и React, а также задаем глобальные переменные для браузера и Node.js. Отключаем некоторые правила для конфигурационных файлов, где использование require является нормой.
Шаг 14: Настраиваем Storybook

Создаем .storybook/main.js:
javascript
/** @type { import('@storybook/react-vite').StorybookConfig } */
const config = {
stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [],
framework: {
name: "@storybook/react-vite",
options: {},
},
};
export default config;
Создаем .storybook/preview.js:
javascript
/** @type { import('@storybook/react-vite').Preview } */
const preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;
Конфигурация Storybook указывает, где искать файлы с историями (stories), и настраивает фреймворк (в нашем случае Vite) для быстрой разработки. Файл preview.js позволяет глобально настраивать параметры отображения всех историй.
?️ Создаем файлы компонентов
Шаг 15: Создаем тип для CSS Modules
src/types/css-modules.d.ts:
typescript
declare module "*.module.css" {
const classes: { [key: string]: string };
export default classes;
}
TypeScript по умолчанию не знает о формате CSS Modules. Этот файл объявляет модуль, сообщая TypeScript, что при импорте *.module.css мы получаем объект, где ключи — это названия классов, а значения — строки. Это избавляет от ошибок типизации при обращении к styles.button.
Шаг 16: Создаем настройку тестов
src/_internal/test/setupTest.ts:
typescript
import "@testing-library/jest-dom";
Этот файл выполняется перед каждым тестовым прогоном. Здесь мы подключаем матчеры из @testing-library/jest-dom (например, toBeInTheDocument()), которые значительно расширяют стандартные возможности утверждений (assertions) в Jest.
Шаг 17: Создаем компонент Button

src/Button/Button.tsx:
typescript
import { ButtonHTMLAttributes, DetailedHTMLProps, FC } from "react";
import styles from "./styles.module.css";
type ButtonProps = DetailedHTMLProps<
ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
>;
const Button: FC<ButtonProps> = (props) => {
return <button {...props} className={styles.button} />;
};
export type { ButtonProps };
export default Button;
src/Button/styles.module.css:
css
.button {
background-color: #4caf50;
border: none;
color: white;
padding: 12px 24px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
border-radius: 8px;
transition: background-color 0.3s;
&:hover {
background-color: #45a049;
}
&:active {
background-color: #3d8b40;
transform: translateY(1px);
}
}
Мы создаем наш первый компонент — кнопку. Это функциональный компонент, который принимает все стандартные свойства HTML‑элемента button. Мы используем CSS Modules для стилизации, что обеспечивает изоляцию стилей и избегает конфликтов имен.
Шаг 18: Создаем тесты для компонента
src/Button/Button.test.tsx:
typescript
import { render, screen, fireEvent } from "@testing-library/react";
import Button from "./Button";
describe("Button Component", () => {
it("renders with correct text", () => {
render(<Button>Click me</Button>);
expect(
screen.getByRole("button", { name: /click me/i }),
).toBeInTheDocument();
});
it("calls onClick when clicked", () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByRole("button"));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("matches snapshot", () => {
const { container } = render(<Button>Save</Button>);
expect(container.firstChild).toMatchSnapshot();
});
});
Пишем модульные тесты, которые проверяют:
Рендерится ли кнопка с переданным текстом;
Вызывается ли переданный обработчик
onClickпри клике;Соответствует ли вывод компонента сохраненному снимку (snapshot), что помогает быстро обнаружить незапланированные изменения в вёрстке.
Шаг 19: Создаем Storybook stories
src/Button/Button.stories.tsx:
typescript
import type { Meta, StoryObj } from "@storybook/react";
import { Button } from "./index";
const meta: Meta<typeof Button> = {
component: Button,
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Basic: Story = {
args: {
children: "Apply",
},
};
Story — это, по сути, изолированный пример использования компонента. В Storybook мы можем визуально представлять наши компоненты, менять их свойства (props) через панель управления и документировать ожидаемое поведение для команды разработки и дизайнеров.
Шаг 20: Настраиваем экспорты
src/Button/index.ts:
typescript
export * from "./Button";
src/index.ts:
typescript
export * from "./Button";
Файлы index.ts — это точка входа в наши модули. Они реэкспортируют наружу только то, что должно быть публичным API нашего компонента и библиотеки в целом. Это позволяет импортировать компоненты удобным способом: import { Button } from 'ui-kit';.
? Запуск и использование

Шаг 21: Добавляем скрипты в package.json
Обновляем package.json:
json
{
"name": "ui-kit",
"version": "1.0.0",
"main": "index.cjs",
"module": "index.js",
"typings": "index.d.ts",
"style": "index.css",
"files": ["*.js", "*.cjs", "*.d.ts", "*.css", "*.map"],
"scripts": {
"clean": "rm -rf dist lib",
"build": "npm run clean && rollup -c --bundleConfigAsCjs",
"pack": "npm run build && mkdir -p dist && cp -r lib/* dist/ && cp package.json dist/ && cd dist && npm pack && mv *.tgz ../ && cd .. && rm -rf dist",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"lint": "eslint",
"format": "prettier . --write",
"test": "jest"
},
"peerDependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0"
}
}
Мы добавляем в package.json удобные npm‑скрипты, которые автоматизируют рутинные задачи: сборку, очистку, запуск дев‑сервера, тестирование, линтинг и упаковку библиотеки в .tgz архив, который можно установить в другом проекте для тестирования.
Шаг 22: Запускаем инструменты разработки
bash
# Сборка библиотеки
npm run build
# Запуск Storybook
npm run storybook
# Запуск тестов
npm run test
# Линтинг
npm run lint
# Форматирование кода
npm run format
# Создание npm пакета
npm run pack
Теперь, когда всё настроено, мы можем пользоваться плодами нашего труда. Эти команды запускают различные процессы разработки, позволяя собирать библиотеку, просматривать и разрабатывать компоненты в Storybook, проверять код и запускать тесты.
? Что мы получили в результате
После выполнения всех шагов у нас есть:
✅ Современная система сборки с Rollup
✅ Поддержка TypeScript с генерацией declaration files
✅ CSS Modules с минификацией и извлечением стилей
✅ Полная система тестирования с Jest и Testing Library
✅ Интерактивная документация со Storybook
✅ Линтинг и форматирование с ESLint и Prettier
✅ Поддержка ESM и CommonJS для широкой совместимости
? Заключение
Мы настроили полнофункциональную среду разработки UI-Kit, которая включает все современные инструменты фронтенд-разработки. Такой подход позволяет:
Быстро разрабатывать новые компоненты
Обеспечивать качество кода через тесты и линтинг
Документировать компоненты для команды разработки
Легко поддерживать и расширять библиотеку
Какие инструменты вы используете в своих UI‑Kit? Делитесь опытом в комментариях!