Сегодня я хочу начать разговор на более техническую тему. Мне всегда было интересно наблюдать, как во фронтенде меняются подходы к стилизации компонентов. Имхо, это вообще один из самых динамично развивающихся и холиварных аспектов разработки. Практически каждый серьёзный сдвиг в экосистеме сопровождается изменениями в том, как мы красим кнопки — и сейчас мы снова оказались в эпицентре такого сдвига.
Классический runtime CSS-in-JS, который был де-факто стандартом для мира React-разработки ещё несколько лет назад, уходит в прошлое. В марте этого года основной контрибьютор самой популярной CSS-in-JS библиотеки
styled-components, Evan Jacobs, объявил о том, что активная разработка проекта прекращается и тот переходит в режим поддержки.Главная причина такого решения — сдвиг React-экосистемы в сторону React Server Components (RSC), которые не поддерживают динамическое внедрение стилей на клиенте, из‑за чего весь runtime‑механизм CSS‑in‑JS оказывается несовместим с новой моделью рендеринга компонентов. Использование RSC требует, чтобы стили вычислялись на этапе сборки и были заранее подготовлены для отображения в браузере.
Это не значит, что от runtime‑решений теперь нужно отказаться совсем — во многих сценариях они по‑прежнему уместны. Для полностью клиентских приложений styled-components, Emotion и основанные на них UI-библиотеки вроде Chakra UI до сих пор остаются отличным выбором. Например, у меня на работе в банке styled-components используется в онлайн-банке, во всех внутренних приложениях и в UI-либе.
Учитывая, что RSC сейчас полноценно поддерживаются только в Next.js, в первую очередь альтернативу нужно искать именно для приложений, написанных на этом фреймворке. Конечно, RSC уже постепенно выходит за пределы Next.js и необходимость в альтернативах будет только увеличиваться.
Давайте для начала определим, что должны учитывать такие альтернативы:
• Build-time extract. Стили должны вычисляться один раз на этапе сборки. Инструменты сборки обязаны уметь генерировать итоговые CSS‑файлы или подключать
<style>‑теги в документ ещё до запуска приложения.• Zero-runtime. В процессе работы приложения не должно происходить генерации новых стилей.
И какие преимущества они дают относительно runtime-решений:
• Существенное ускорение работы и Time to Interactive. Нет никаких вычислений стилей во время рендера, интерфейс становится доступен быстрее.
• Меньший размер JS‑бандла. Логика работы со стилями обрезается на этапе сборки, в клиент попадает только готовый CSS.
• Совместимость с Concurrent Rendering. Стили не зависят от жизненного цикла компонентов и не ломают рендеринг при прерываниях или откатах.
Итак, какие zero-runtime альтернативы есть на данный момент?
Самое популярное решение прямо сейчас — это Tailwind, в котором стили задаются не в коде компонента, а через заранее сгенерированный набор utility‑классов. Помимо Tailwind, всё ещё активно используется классическая связка CSS Modules + PostCSS. Этот подход максимально близок к «чистому» CSS и максимально надёжен: он не зависит от конкретного фреймворка и вряд ли когда‑то потеряет актуальность.
Оба этих решения можно отнести к class‑based подходам, и они довольно далеки от парадигмы CSS‑in‑JS. Я не хочу сказать, что эти подходы плохие, но для меня очевидно, что есть много аспектов, в которых CSS-in-JS выигрывает у class‑based решений:
• Разработчик не думает о классах, а только о стилях компонента и логике работы с ними.
• Компонент становится самодостаточной сущностью, где код и стили связаны напрямую.
• Всё управление внешним видом идёт через API, а не через ручную комбинацию классов.
• В связке с TypeScript такие компоненты дают автодополнение, автодокументацию и проверку корректности ещё на этапе сборки.
• Современные CSS‑in‑JS решения позволяют управлять темами через CSS‑переменные и строго типизированные токены.
➖➖➖
В следующих постах мы подробнее рассмотрим актуальные zero-runtime CSS-in-JS библиотеки, сравним их между собой, обсудим подходы, которые они используют и перспективы их развития.