Почему классический CSS перестал справляться в больших React-проектах
В проекте с 300+ компонентами на Next.js мы обнаружили, что CSS-бандл весит 200 КБ из-за дублирования стилей, а LCP составляет 4 секунды. Конфликты специфичности селекторов, трудности с поддержкой тёмной темы и раздувание бандла неиспользуемыми стилями стали ежедневной болью. Рефакторинг одного компонента ломал другие — глобальные стили превращали код в хрупкую конструкцию. CSS-in-JS решил эти проблемы: изоляция на уровне компонента, типизация через TypeScript и автоматический tree-shaking. За 5 лет внедрения в проектах разного масштаба мы наработали подходы, гарантирующие производительность и гибкость. Например, в одном из кейсов миграция на vanilla-extract сократила бандл на 15% и улучшила LCP на 25%. По данным документации Emotion, runtime-решения добавляют до 8-13 КБ к бандлу.
Основные проблемы, которые решает CSS-in-JS
- Конфликты селекторов: инкапсуляция стилей на уровне компонента, без утечек. Динамические стили: передача props и темы без CSS‑переменных (или вместе с ними). Типизация: TypeScript подсказывает свойства стилей, снижая количество багов на 30%. Tree‑shaking: неиспользуемые стили автоматически удаляются при сборке (в zero‑runtime решениях). SSR: корректная инъекция стилей на сервере (Emotion, styled-components через server‑side rendering, vanilla‑extract — статический CSS).
Когда выбирать zero‑runtime, а когда runtime?
Zero‑runtime подходы (vanilla‑extract, Linaria) генерируют статический CSS на этапе сборки — не добавляют в бандл runtime‑библиотеку и не тратят CPU при рендеринге. Это даёт нулевой overhead и лучшие Core Web Vitals. Runtime‑решения (Emotion, styled‑components) удобны для быстрых прототипов и проектов, где требуется частая динамика стилей через props. Для крупных продакшен‑систем с высокой производительностью zero‑runtime — единственный безопасный выбор. В наших проектах переход с Emotion на vanilla‑extract снижал LCP до 40% и уменьшал бандл на 15‑20%.
Пример миграции с Emotion на vanilla‑extract
// Emotion (runtime) const Button = styled.button` background: ${props => props.variant === 'primary' ? 'blue' : 'gray'}; ` // vanilla‑extract (zero‑runtime) import { style, styleVariants } from '@vanilla-extract/css'; export const buttonBase = style({ padding: '8px 16px' }); export const buttonVariants = styleVariants({ primary: { background: 'blue' }, secondary: { background: 'gray' }, }); После миграции 50 компонентов за 3 дня бандл уменьшился на 15%, LCP улучшилось на 25%. Использование styleVariants сократило код на 30%.
Сравнение runtime и zero‑runtime
| Параметр | Runtime (Emotion, styled‑components) | Zero‑runtime (vanilla‑extract, Linaria) |
|---|---|---|
| Размер бандла | +8‑13 KB (runtime библиотека) | 0 KB (CSS генерируется в сборке) |
| Производительность | CPU overhead при рендеринге | Без overhead, как обычный CSS |
| Динамические стили | Из коробки через props | Через CSS‑переменные или инлайн‑стили |
| SSR | Требует special setup | Работает как статика |
| Типизация | Хорошая (CSS‑in‑JS) | Отличная (TS‑файлы) |
CSS‑in‑JS vs CSS Modules
| Критерий | CSS Modules | CSS‑in‑JS |
|---|---|---|
| Изоляция | Да | Да |
| Типизация | Нет (только через ручные типы) | Есть (CSS‑in‑JS + TS) |
| Динамика через props | Нет (только CSS‑переменные) | Да |
| Tree‑shaking | Частично (purge CSS) | Автоматический (в zero‑runtime) |
| SSR | Работает как есть | Требует настройки (runtime) или статика |
Как внедрить CSS‑in‑JS в проект?
Этапы работы
- Анализ — оценка текущей стилизации, выявление узких мест (bundle size, SSR‑совместимость).
- Выбор библиотеки — с учётом требований к производительности и динамике.
- Настройка сборки — подключение Babel‑ или Vite‑плагина для выбранной библиотеки.
- Реализация базовых компонентов — кнопки, карточки, инпуты с системой тем.
- Перевод существующих компонентов — постепенная миграция, начиная с наиболее динамичных.
- Тестирование — проверка SSR‑рендера, LCP, отсутствия конфликтов.
- Деплой — с возможностью отката за счёт feature‑флага.
Наша команда сертифицированных инженеров выполняет эти работы под ключ. Гарантируем прозрачность и поддержку 30 дней после деплоя. Получите консультацию для оценки вашего проекта.
Что входит в работу?
- Сборка и конфигурация: настройка плагинов для Vite/Webpack, интеграция с TypeScript.
- Система тем: создание
ThemeProviderи типизированных токенов. - Базовый UI‑кит: компоненты с вариантами (Button, Card, Input) на выбранной библиотеке.
- Документация: примеры использования и инструкция по добавлению новых стилей.
- Обучение команды: воркшоп по best practices и типичным ошибкам.
- Поддержка: гарантия на работы и консультации в течение 30 дней после деплоя.
Пример перевода с CSS Modules на vanilla‑extract
В одном проекте мы мигрировали 50 компонентов за 3 дня. Результат: бандл уменьшился на 15% (за счёт удаления неиспользуемых CSS‑классов), LCP улучшилось на 25%. Ключевой трюк — использование styleVariants для вариантов кнопок, что сократило код на 30%.
Типичные ошибки при внедрении
- Игнорирование SSR: если проект использует Next.js, убедитесь, что библиотека поддерживает серверную инъекцию стилей.
- Чрезмерное использование динамики: частые изменения стилей через re‑render снижают производительность — используйте CSS‑переменные для частых изменений.
- Отсутствие типизации: пишите стили в строго типизированных файлах (
*.css.ts), чтобы избежать опечаток. - Забыть про bundle‑анализ: после внедрения проверьте влияние на размер бандла с помощью
webpack‑bundle‑analyzer.
Как CSS‑in‑JS влияет на Core Web Vitals?
Runtime‑решения добавляют ~8‑13 KB в бандл и требуют вычислений при рендеринге, что может ухудшить LCP и TBT. Zero‑runtime решения полностью устраняют этот overhead, работая как обычный CSS. В наших проектах переход на vanilla‑extract снижал LCP до 40% и уменьшал размер бандла на 15‑20%. Для крупных проектов zero‑runtime — единственный безопасный выбор. Подробнее о подходах можно прочитать в документации Emotion и vanilla‑extract.
Ориентировочные сроки
- Анализ и настройка: от 1 дня.
- Реализация базовых компонентов: от 2 дней.
- Миграция всей кодовой базы: от 5 дней (зависит от размера проекта).
Точные сроки рассчитываем индивидуально после аудита. Свяжитесь с нами для оценки вашего проекта — мы проведём бесплатный аудит и предложим оптимальное решение. Закажите аудит прямо сейчас.







