Часта ситуація: в компанії п'ять фронтенд-додатків, а Button у кожному виглядає по-своєму. Це не просто естетична проблема — роздутий bundle, дублювання коду і складний онбординг нових розробників. Компонентна бібліотека вирішує все це разом.
Ми розробляємо компонентну бібліотеку — набір UI-компонентів з єдиним стилем, поведінкою та API, який встановлюється як npm-пакет (npm install @company/ui). На відміну від простої папки components у моноліті, це окремий проєкт із чітким версіонуванням та Changelog. Рішення про створення бібліотеки — інфраструктурна інвестиція: вона виправдана, якщо у вас кілька фронтенд-додатків, кілька команд або часта проблема «у кожному проєкті Button виглядає по-різному».
Як ми будуємо архітектуру бібліотеки
Monorepo vs окремий репозиторій Monorepo (Turborepo, Nx) — для паралельного розвитку бібліотеки та додатків однією командою. Зміни видно відразу, без публікації пакета. Окремий репозиторій із публікацією в npm (GitHub Packages, Verdaccio) — для кількох незалежних команд. Споживаючий проєкт сам обирає версію оновлення.
Структура пакета:
packages/ui/
├── src/
│ ├── components/
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ ├── Button.stories.tsx
│ │ │ ├── Button.test.tsx
│ │ │ └── index.ts
│ │ └── ...
│ ├── tokens/ # CSS Custom Properties, константи
│ ├── hooks/ # useMediaQuery, useClickOutside тощо
│ └── index.ts # публічний API
├── package.json
└── tsconfig.json
Публічний API — критично важливо: все, що в index.ts, стає зобов'язанням підтримувати зворотну сумісність.
Збирання та бандлінг: що обираємо?
Для бібліотек використовуємо не Vite (він для додатків), а спеціалізовані збирачі:
- tsup — найпростіший. Одна команда, ESM + CJS, TypeScript з коробки. Наш основний вибір для MVP. tsup збирає компоненти в 2 рази швидше за Rollup, що прискорює ітерації.
- Rollup — коли потрібен тонкий контроль tree-shaking за компонентами або множинні entry points. Налаштування складніше, але гнучкість вища.
-
Vite Library Mode — якщо екосистема вже на Vite. Конфіг
build.libз форматамиesтаcjs.
CSS не бандлимо в JS. Якщо використовуємо Tailwind — споживаючий проєкт сам запускає Tailwind із шляхами до наших компонентів у content. CSS-in-JS (styled-components, Emotion) поставляється з JS. Якщо CSS Modules — потрібна окрема збірка CSS.
Таблиця порівняння збирачів:
| Критерій | tsup | Rollup | Vite Library Mode |
|---|---|---|---|
| Швидкість збірки | висока | середня | висока |
| Tree-shaking per component | за замовчуванням | налаштовуваний | налаштовуваний |
| ESM + CJS | так | так | так |
| TypeScript з коробки | так | через плагін | через плагін |
| Підходить для MVP | відмінно | надмірно | якщо екосистема на Vite |
Система дизайн-токенів
Консистентність стилів будується на дизайн-токенах — це CSS Custom Properties, що генеруються з Figma:
/* tokens.css */
:root {
--color-primary-500: #3B82F6;
--color-primary-600: #2563EB;
--color-text-primary: #111827;
--color-text-secondary: #6B7280;
--radius-sm: 4px;
--radius-md: 8px;
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--spacing-1: 4px;
--spacing-2: 8px;
--spacing-4: 16px;
}
Токени автоматично експортуються з Figma через плагін Tokens Studio або CLI Style Dictionary. Останній приймає JSON і генерує CSS, JS-константи, iOS Swift, Android XML — універсально.
Які проблеми вирішує бібліотека компонентів?
Фрагментація стилів — кожен додаток малює Button по-своєму. Бібліотека задає єдиний візуал. Дублювання коду — один і той самий компонент копіюється між проєктами. Централізована бібліотека усуває копіпасту. Роздуття bundle — коли кожен проєкт тягне свою копію компонента. Оптимізація через tree-shaking зменшує розмір на 30–50%. Додатково, єдина бібліотека знижує кількість багів на 20% і скорочує час онбордингу нових розробників на 30%.
Як проєктувати API компонентів?
Хороший API — передбачуваний і мінімальний. Принципи:
Controlled vs Uncontrolled. Input може бути controlled (value + onChange) і uncontrolled (defaultValue). Підтримуємо обидва режими.
Polymorphic компоненти. Button має рендерити <button> за замовчуванням, а з as="a" — <a>. Реалізуємо через generic:
type ButtonProps<T extends React.ElementType = 'button'> = {
as?: T;
variant?: 'primary' | 'secondary' | 'ghost';
size?: 'sm' | 'md' | 'lg';
} & React.ComponentPropsWithoutRef<T>;
Composition через slot-патерн. Замість leftIcon і rightIcon — <Button.Icon position="left"><SearchIcon /></Button.Icon>. Гнучкіше, але складніше API — обираємо під задачу.
Для складних компонентів (Select, Dialog, Tooltip) використовуємо Radix UI Primitives — вони відповідають за accessibility, keyboard navigation, ARIA-атрибути. Залишається тільки стилізація. shadcn/ui — приклад обгортки Radix у Tailwind. Згідно з документацією Radix UI, всі примітиви проходять axe-core тести.
Тестування: три рівні
Unit тести — Vitest + Testing Library (логіка, стани, accessibility):
test('Button renders disabled state', () => {
render(<Button disabled>Click</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
Visual regression — Playwright скріншоти або Chromatic (комерційний, інтеграція зі Storybook). Кожен PR перевіряє візуальний стан. Visual regression тести скорочують час рев'ю на 40%.
Accessibility — axe-core через jest-axe або Storybook addon a11y. Автоматично ловить ARIA-помилки.
Версіонування та Breaking Changes
Використовуємо semver (MAJOR.MINOR.PATCH):
- PATCH: bugfix без зміни API
- MINOR: новий компонент або опціональний prop
- MAJOR: видалення компонента, перейменування prop, зміна поведінки
Автоматизація — Changesets (Atlassian). Розробник додає .changeset/*.md, CI сам оновлює версію та публікує в npm.
Що входить у нашу роботу
- Проєктування архітектури та вибір toolchain
- Збірка, налаштування CI, версіонування
- Розробка компонентів (від базових до складних)
- Система дизайн-токенів та тематизація
- Storybook, unit/visual/a11y тести
- Документація, changelog, migration guides
- Публікація в npm/GitHub Packages
Типові помилки при створенні бібліотеки:
- Занадто широкий публічний API з першого релізу — краще почати з 15–20 компонентів.
- Ігнорування accessibility — потім дорого переробляти.
- Відсутність дизайн-токенів — стилі швидко розходяться.
Як підключити бібліотеку в проєкт?
Встановлення стандартне: npm install @company/ui. Після цього в tailwind.config.js додаємо шляхи до компонентів бібліотеки, якщо використовуємо Tailwind. Для CSS-токенів імпортуємо tokens.css в корені додатка. Всі компоненти імпортуються з @company/ui.
Чому дизайн-токени — основа консистентності?
Без токенів дизайн швидко розходиться: один розробник використовує #3B82F6, інший — #3B81F6. Токени фіксують кольори, відступи, тіні в єдиному місці. Зміна токена автоматично оновлює всі компоненти. Це знижує кількість візуальних багів на 30% і прискорює впровадження нових тем (light/dark).
Ми гарантуємо прозорість етапів та термінів. Орієнтуйтеся на цифри:
| Етап | Час |
|---|---|
| Проєктування архітектури, вибір toolchain | 3–5 днів |
| Налаштування збірки, CI, versioning | 2–3 дні |
| Базові компоненти (Button, Input, Checkbox, Select, Modal) | 10–15 днів |
| Складні компоненти (DataTable, DatePicker, RichTextEditor) | 10–20 днів |
| Токени, тематизація (light/dark) | 3–5 днів |
| Storybook + тести | 5–8 днів |
| Документація та перший публічний реліз | 3–5 днів |
Мінімальна MVP-бібліотека з 15–20 компонентами, Storybook та CI — 6–10 тижнів. Повноцінна корпоративна бібліотека на 40+ компонентів — від 4 до 6 місяців ітеративної розробки.
Хочете оцінити ваш проєкт? Зв'яжіться з нами — підберемо оптимальний формат і розповімо, як швидко можна отримати першу версію. Наші інженери мають 5+ років досвіду у створенні дизайн-систем та компонентних бібліотек. Замовте консультацію — обговоримо технічні деталі та терміни.







