
В этой статье мы подробно разберем процесс настройки 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? Делитесь опытом в комментариях!
