Этот документ содержит углубленный анализ всех текущих решений CSS-In-JS , которые поддерживают рендеринг на стороне сервера и TypeScript .
Базовая ссылка, которую мы будем использовать для сравнения, - это подход CSS Modules .
Мы используем Next.js в качестве структуры SSR для строительства ресурсов.
Последним важным аспектом является безопасность типа с полной поддержкой типографии .
? Последнее обновление: август 2021
? Чтобы получить более короткий обзор, вы можете проверить статью о трюках CSS :
https://css-tricks.com/a-thorough-analysis-of-css-in-js/
? Если вы предпочитаете видео вместо этого, вы можете проверить мой разговор от ngpartycz :
https://www.youtube.com/watch?v=c7uwghrax9a
Пожалуйста, проверьте наши цели и отказ от ответственности, прежде чем сделать выводы.
Язык CSS и CSS-модули имеют некоторые ограничения, особенно если мы хотим иметь безопасный код. Некоторые из этих ограничений имеют альтернативные решения, другие просто раздражают или не идеальны :
Стили нельзя совпадать с компонентами
Это может быть разочаровывающим при создании многих небольших компонентов, но это не нарушатель сделки. Тем не менее, опыт перемещения обратно и власти между файлом component.js и файлом component.css , поиск данного имени класса и невозможность легко «перейти к определению стиля» , является важным недостатком производительности.
Стилирование псевдо и медиа -запросы требует дублирования селектора
Другим разочаровывающим фактом является необходимость дублировать наши классы CSS при определении псевдо классов и элементов или медиа -запросов . Мы можем преодолеть эти ограничения, используя препроцессор CSS, такой как SASS, меньше или стилус , который поддерживает селектор & родителей, обеспечивая контекстный стиль .
. button {}
/* duplicated selector declaration for pseudo classes/elements */
. button : hover {}
. button :: after {}
@media ( min-width : 640 px ) {
/* duplicated selector declaration inside media queries */
. button {}
} Использование стилей отключено от их определения
Мы не получаем Intellisense с модулями CSS, что классы CSS определяются в файле component.css , что делает копию вставки необходимым инструментом, снижая DX. Это также делает рефакторинг очень громоздким из -за отсутствия безопасности.
Использование токенов Design-Safe-Safe в CSS нетривиально
Любые токены дизайна, определенные в JS/TS (для получения выгоды от безопасности типов), не могут быть непосредственно использованы в CSS.
Для этого вопроса есть как минимум 2 обходных пути, ни один из них не элегантен:
.module.css ..css .Есть конкретные цели, которые мы ищем с этим анализом:
Став еще более конкретно, мы хотели испытать использование различных решений CSS-In-JS, касающихся:
props (AKA. Варианты компонентов) или от пользовательского вводаЭтот анализ предназначен для объективного и неопинированного :
? Что ты не найдешь здесь?
? Что вы найдете здесь?
Библиотеки не представлены в каком -либо конкретном порядке. Если вы заинтересованы в краткой истории CSS-In-JS , вам следует проверить прошлое, настоящее и будущее проницательного разговора CSS-In-JS от Макса Стобера.
| 1. Совместное выполнение | 2. DX | 3. tag` ` | 4. { } | 5. ts | 6. & CTX | 7. гнездование | 8. Тема | 9. .css | 10. <style> | 11. Атомный | 12. className | 13. <Styled /> | 14. css Prop | 15. Агностик | 16. Дельта страниц размер | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CSS -модули | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | - | |||||||||
| Стиль JSX | ✅ | ? | ✅ | ? | ✅ | ✅ | ✅ | +2.8 kB / +12.0 kB | ||||||||
| Стилизованные компоненты | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +13.4 kB / +39.0 kB | ||||
| Эмоция | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +6.5 kB / +20.0 kB | ||
| Типичный | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | ✅ | +2.1 kB / +8.0 kB | |||||
| Фела | ✅ | ? | ? | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +11.9 kB / +43.0 kB | |||
| Швы | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +5.3 kB / +17.0 kB | |||
| JSS | ✅ | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +18.2 kB / +60.0 kB | |||
| Губер | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +1.1 kB / +4.0 kB | ||
| Составлено | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | +3.5 kB / +9.0 kB | |||
| Линария | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +2.7 kB / +6.0 kB | ||||
| ваниль-экстракт | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | +0.0 kB / -2.0 kB |
Возможность определять стили в том же файле, что и компонент. Обратите внимание, что мы также можем извлечь стили в отдельный файл и импортировать их, если мы предпочитаем его.
⬆ для обзора
Относится к опыту разработчика , который включает в себя 2 основных аспекта:
⬆ для обзора
tag` ` (Tagged шаблоны)Поддержка определения стилей как строки , используя шаблоны с меткой ES:
kebab-case для названий свойств, как и простой синтаксис CSS;string ;⬆ для обзора
{ } (стили объекта)Поддержка определения стилей как объектов , используя простые объекты JavaScript:
camelCase для названий свойств, как мы бы сделали в React Native;⬆ для обзора
Поддержка TypeScript, встроенный или через пакет @types , который должен включать:
Props Generics, где это применимо (получить доступ к типам доступа к типам компонентов репута при определении динамических стилей);⬆ для обзора
& CTX (контекстуальные стили)Поддержка контекстных стилей, позволяющая нам легко определять псевдо классы и элементы и медиа -запросы без необходимости повторять селектор, как требуется в простом CSS:
& Parent Selector;⬆ для обзора
Поддержка произвольных вложенных селекторов :
⬆ для обзора
Встроенная поддержка тематических или управляющих токенов для системы проектирования.
Мы не проверили эту функцию , поэтому мы только делаем заметки, какие библиотеки выражают свою поддержку в своих документах.
⬆ для обзора
.css (статическое извлечение CSS) Определенные стили извлекаются в виде статических файлов .css :
⬆ для обзора
<style> тег Определенные стили вводится внутри <style> в тегах в документе <head> :
⬆ для обзора
Способность генерировать атомные классы CSS , тем самым увеличивая возможности повторного использования в стиле и уменьшая дублирование:
⬆ для обзора
className Библиотечный API возвращает string , которую мы должны добавить в наш компонент или элемент;
⬆ для обзора
<Styled /> API создает компонент обертки (или Styled ), который включает в себя сгенерированное className (ы):
StyledButton или StyledList вместо постоянных, таких как button_styles или list_styles , так что относительно именования это почти одно и то же;⬆ для обзора
css Prop Позволяет проходить стили, используя специальную поддержку css , аналогично тому, как мы определили бы встроенные стили, но библиотека генерирует уникальное имя класса CSS за кулисами:
⬆ для обзора
Позволяет использовать без или с любой структурой. Некоторые библиотеки построены специально только для реагирования.
Примечание . Некоторые библиотеки, такие как стежки или эмоциональный документ, реагируют только на использование, хотя у них есть ядро , которое является агностиком.
⬆ для обзора
Общая разница в размерах страниц в KB (передано Gzippup & Minified / Compressed & Minished) по сравнению с модулями CSS , для всей сборки производства страниц индекса с использованием next.js:
Примечание. Все сборки были сделаны с помощью следующей.js 11.1.0 , и значения взяты с вкладки Chrome Devtools Network, передаваемые по размеру сети против ресурса.
⬆ для обзора
Следующие наблюдения применяются для всех решений (за незначительными заостренными исключениями).
Компоненты, используемые только на определенном маршруте, будут в комплекте только для этого маршрута. Это то, что Next.js выполняет вне коробки.
Все решения предлагают способ определить глобальные стили, некоторые с выделенным API.
Все решения предлагают поддержку рендеринга на стороне сервера и легко интегрируются с next.js.
Все решения автоматически добавляют конкретные префиксы поставщиков из коробки.
Все решения генерируют уникальные имена классов, как и модули CSS. Алгоритм, используемый для генерации этих имен, сильно меняется между библиотеками:
.heading библиотеки используют алгоритм хэширования , требующий большего количества вычислений, но приводящие к идентификационным именам Card .Card_heading_h7Ys5 :..heading-0-2-1 , .input-0-2-2 ), либо буквы алфавита ( a, b, c, ... aa, ab, ac и т. Д.), что делает этот подход более эффективным, но приводя к неапотентным именам классов (не может выяснить, не могут ли это выяснить какие-либо потенциальные недостатки); Ни одно из решений генерирует встроенные стили, который является более старым подходом, используемым Radium & Glamour. Подход менее эффективен, чем классы CSS, и он не рекомендуется в качестве основного метода определения стилей. Это также подразумевает использование обработчиков событий JS для запуска псевдо -классов, поскольку встроенные стили не поддерживают их. Видимо, все современные решения в настоящее время отошли от этого подхода.
Все решения поддерживают большинство свойств CSS, которые вам понадобятся: псевдо классы и элементы , медиа -запросы и ключевые кадры - это те, которые мы протестировали.
Большинство решений продают сами как способные «извлекать критические CSS» во время SSR. Обратите внимание, что это не относится к критическому извлечению CSS выше, как мы первоначально думали.
Что они на самом деле делают:
Со 100% статическим CSS на самом деле не будет никакой пользы. Благодаря динамическим страницам, которые отображают очень мало элементов на сервере, и большинство компонентов динамично отображаются на клиенте, выгода увеличивается.
Исключение : библиотеки, которые используют статическое извлечение CSS.
Понимание того, как эти функции влияют на основные виды вещей и показатели производительности в целом, является чрезвычайно важным фактором, и способ, которым стили доставляются клиенту, вероятно, оказывает наибольшее влияние, поэтому давайте подробно проанализируем это.
Кроме того, есть 2 разных сценария, которые мы должны рассмотреть:
.js , .css , Media и т. Д.); .css извлечение файла Решения, которые генерируют статические файлы .css , которые вы обычно включали бы как <link> теги (ы) в <head> вашей страницы, в основном представляют ресурсы блокировки. Это сильно влияет на FCP , LCP и любую другую метрику, которая следует.
? Пустой кеш
Если у пользователя есть пустой кеш, должно произойти следующее, отрицательно влияя на FCP и LCP :
<body> , даже если весь HTML уже загружен, и он может быть даже с нетерпением анализирован, а некоторые ресурсы уже извлечены заранее; Это правда, что вы можете получить параллельные другие ресурсы <head> (дополнительные файлы .css или .js ), но это, как правило, плохая практика;
? Полный кеш
Однако во время последующих посещений весь ресурс .css будет кэширован, поэтому FCP и LCP будут положительно повлияли.
Ключевые моменты
Это решение, кажется, лучше подходит, когда:
.css , который можно кэшировать при посещении других страниц;<style> стили впрыскиваемых тегов Во время SSR стили будут добавлены в виде тегов <style> в <head> страницы. Имейте в виду, что они обычно не включают все стили, необходимые для страницы, потому что большинство библиотек выполняют критическую извлечение CSS, поэтому эти styles должны быть обычно меньше, чем весь статический файл .css , который обсуждался ранее.
? Пустой кеш
Поскольку мы отправляем меньше байтов CSS, и они вставлены в файл .html , это приведет к более быстрому FCP и LCP :
.css , поэтому браузер не заблокирован;.js в конце документа, <head> не будет выполнять никаких запросов, поэтому рендеринг будет происходить очень быстро;.css :.js вместе с компонентами, во время гидратации (это включает в себя все критические CS, уже отправленные внутри <style> Tag + другие); ? Полный кеш
Когда кеш пользователя заполнен, дополнительные файлы .js не требуют извлечения, поскольку они уже кэшируются.
Однако, если страница является SSRED , вставленные критические CSS, отображаемые в теге <style> документа, будут загружены снова, если только мы не сталкиваемся со статическим HTML, который также может быть кэширован, или мы имеем дело с кэшированием HTML на нашей инфраструктуре.
Но по умолчанию мы будем отправлять дополнительные байты на каждой странице HTTP -запроса, независимо от того, кэшируется или нет.
Ключевые моменты
Это решение, кажется, лучше подходит, когда:
Большинство решений говорят, что они удаляют неиспользованный код/стили . Это только наполовину .
Неиспользуемый код действительно сложнее накапливаться, особенно если вы сравниваете его с простыми файлами .css , как мы писали десять лет назад . Но по сравнению с CSS -модулями различия не такие большие. Любое решение, которое предлагает возможность определения произвольных селекторов или вложенных стилей, будет объединить их, независимо от того, используются ли они или нет в нашем компоненте. Нам удалось отправить неиспользованные стили SSR со всеми тестируемыми решениями.
Истинное и полное неиспользованное удаление кода трудно реализовать, так как синтаксис CSS не проверяется тип и не анализируется. Кроме того, динамическая природа компонентов делает его практически невозможным в определенных сценариях, особенно когда разметка динамически отображается:
& span : Поточные элементы;&:nth-child() : определенные псевдо-селекторы;& .bg-${color} : динамические селекторы;.parent & : родители селекторов;По сути, мы получаем удаление кода, когда мы удаляем компонент, или мы больше не импортируем его. Это неявное поведение, потому что стили являются прямой зависимостью компонента. Когда компонент исчез, так же как и его стили.
Есть 2 метода для введения CSS в DOM и обновить его из JavaScript:
<style> теги (ы) Этот подход подразумевает добавление одного или нескольких тегов <style> в DOM (либо в <head> , либо где -то в <body> ), используя .appendChild (), чтобы добавить узел <style> , в дополнение к любому .TextContent, .innerHtml для обновления тега <style> .
<style> и обновления всего его контента может быть медленным, чтобы обновлять весь DOM, когда мы действительно изменили только крошечный набор правил (ы) CSS;DEVELOPMENT , потому что оно обеспечивает лучший опыт отладки;PRODUCTION также; CSSStyleSheet Впервые используемый JSS , этот метод использует CSSStyleSheet.insertRule() для внедрения правил CSS непосредственно в CSSOM .
<style> ;<style> ;$0 в Chrome Devtools (или получите ссылку на него любым другим способом, используя DOM API);.sheet.cssRules на теге <style> , чтобы увидеть массив правил CSS, которые он содержит;PRODUCTION ;DEVELOPMENT ; Если один и тот же компонент импортируется двумя разными маршрутами, он будет дважды отправлять клиенту. Это, безусловно, ограничение системы Bundler/Build, в нашем случае Next.js, и не связано с решением CSS-In-JS .
В next.js рассеяние кода работает на уровне маршрута, объединяя все компоненты, необходимые для конкретного маршрута, но в соответствии с их официальным блогом и Web.DEV, если компонент используется более чем на 50% страниц, его следует включить в комплект commons . Однако, в нашем примере, у нас есть 2 страницы, каждая из которых импортирует компонент Button , и он включен в каждую страницу, а не в пакет commons . Поскольку код, необходимый для стиля, связан с компонентом, это ограничение также повлияет на стили, поэтому стоит помнить об этом.
Это хорошо известный, зрелый и солидный подход. Без сомнения, это значительное улучшение по сравнению с BEM, SMACCS, OOCSS или любой другой методологией CSS для структуры и организации наших CSS, особенно в приложениях на основе компонентов.
Запущен в 2015 году | Вернемся к обзору
✅ Завершение контекстного кода
✅ Структура агностик
Нет стилей/компонентов
Нет поддержки типографии
Нет атомных CSS
Никакой тематической поддержки
Стили определения метода (ы)
Стили в гнездование
Стили применить метод (ы)
classNamestyled компонентcss PropСтили вывод
.css Извлечение файла<style> инъекция тегаЭто базовая линия, которую мы рассмотрим при сравнении всех следующих решений CSS-In-JS . Проверьте мотивацию, чтобы лучше понять ограничения такого подхода, который мы пытаемся заполнить.
| Передается / gzipped | Несомненно | |
|---|---|---|
| Индексный размер страницы | 76,7 КБ | 233 КБ |
Page Size First Load JS
┌ ○ / 2.19 kB 68.7 kB
├ └ css/1d1f8eb014b85b65feee.css 450 B
├ /_app 0 B 66.5 kB
├ ○ /404 194 B 66.7 kB
└ ○ /other 744 B 67.2 kB
└ css/1c8bc5a96764df6b92b4.css 481 B
+ First Load JS shared by all 66.5 kB
├ chunks/framework.895f06.js 42 kB
├ chunks/main.b2b078.js 23.1 kB
├ chunks/pages/_app.40892d.js 555 B
├ chunks/webpack.ddd010.js 822 B
└ css/a92bf2d3acbab964f6ac.css 319 B
Очень простое решение, у него нет специального веб -сайта для документации, все на Github. Это не популярно, но это встроенное решение в next.js.
Версия: 4.0 | Поддерживается Vercel | Запущен в 2017 году | Просмотреть документы | ... вернуться к обзору
✅ Стили/компонент совместное размещение
? Завершение кода контекста : чтобы получить выделение синтаксиса и завершение кода, требуется расширение редактора
? Поддержка TypeScript : @types может быть установлена, но API слишком минимален, чтобы требовать TS
Нет атомных CSS
Никакой тематической поддержки
Не рамочная агностик
Стили определения метода (ы)
Стили в гнездование
Стили применить метод (ы)
classNamestyled компонентcss PropСтили вывод
.css извлечение файла<style> Инъекция тега elements HTML, и это генерирует уникальные имена классов для них (хотя не уверен, что это хорошая практика) В целом, мы чувствовали себя как писать простые CSS, с дополнительным преимуществом того, что способность определять стили вместе с компонентом, поэтому нам не нужен дополнительный файл .css . Действительно, это философия библиотеки: поддержка синтаксиса CSS в файле компонентов. Мы можем использовать любые константы JS/TS функций с интерполяцией строки. Работать с динамическими стилями довольно простой, потому что в конце концов это простой JavaScript. Мы получаем все эти преимущества по очень низкой цене, с довольно маленькими накладными расходами .
Недостатки - это общий опыт написания простых CSS. Без гнездования поддержки псевдо классов/элементов и медиа -запросов становятся довольно громоздкими для управления.
| Передается / gzipped | Несомненно | |
|---|---|---|
| Индексный размер страницы | 79,5 КБ | 245 КБ |
| против CSS -модулей | +2,8 кб | +12 КБ |
Page Size First Load JS
┌ ○ / 2.65 kB 72.6 kB
├ /_app 0 B 70 kB
├ ○ /404 194 B 70.2 kB
└ ○ /other 1.18 kB 71.2 kB
+ First Load JS shared by all 70 kB
├ chunks/framework.895f06.js 42 kB
├ chunks/main.b2b078.js 23.1 kB
├ chunks/pages/_app.a4b061.js 4.12 kB
└ chunks/webpack.61f1b6.js 778 B
Конечно, одно из самых популярных и зрелых решений, с хорошей документацией. Он использует тегированные шаблоны для определения стилей по умолчанию, но также может использовать объекты. Это также популяризировало styled подход компонентов, который создает новый компонент вместе с определенными стилями.
Версия: 5.3 | Поддерживается Максом Стобером и другими | Запущен в 2016 году | Просмотреть документы | ... вернуться к обзору
✅ Стили/компонент совместное размещение
✅ Поддержка TypeScript : @types должен быть установлен с добавлением, через определенно
✅ Встроенная тематическая поддержка
✅ Структура агностик
? Завершение кода контекста : требуется расширение/плагин редактора
Нет атомных CSS
Стили определения метода (ы)
Стили в гнездование
Стили применить метод (ы)
classNamestyled компонентcss PropСтили вывод
.css извлечение файла<style> Инъекция тега Props и т. Д.) Styleled Components предлагает новый подход к компонентам стиля, используя styled -метод, который создает новый компонент, включая определенные стили. Нам не хочется писать CSS, поэтому поступают из модулей CSS, нам придется выучить новый, более программы, чтобы определить стили. Поскольку это позволяет как string , так и object -синтаксис, это довольно гибкое решение как для миграции наших существующих стилей, так и для запуска проекта с нуля. Кроме того, сопровождающие проделали довольно хорошую работу, не отставая от большинства инноваций в этой области.
Однако перед тем, как его принять, мы должны знать, что он поставляется с определенной стоимостью для нашего размера пакета.
| Передается / gzipped | Несомненно | |
|---|---|---|
| Индексный размер страницы | 90,1 КБ | 272 КБ |
| против CSS -модулей | +13.4 КБ | +39 кб |
Page Size First Load JS
┌ ○ / 2.52 kB 83.1 kB
├ /_app 0 B 80.6 kB
├ ○ /404 194 B 80.8 kB
└ ○ /other 1.06 kB 81.7 kB
+ First Load JS shared by all 80.6 kB
├ chunks/framework.895f06.js 42 kB
├ chunks/main.b2b078.js 23.1 kB
├ chunks/pages/_app.731ace.js 14.7 kB
└ chunks/webpack.ddd010.js 822 B
Вероятно, самое всеобъемлющее, полное и софистированное решение. Подробная документация, полностью построенная с TypeScript, выглядит очень зрелой, богатой функциями и в хорошем состоянии.
Версия: 11.4 | Поддерживается Митчеллом Гамильтоном и другими | Запущен в 2017 году | Просмотреть документы | ... вернуться к обзору
✅ Стили/компонент совместное размещение
Support Поддержка TypeScript
✅ Встроенная тематическая поддержка
✅ Завершение контекстного кода : для использования подхода styled компонентов требуется дополнительный плагин редактора
✅ Структура агностик
Нет атомных CSS
Стили определения метода (ы)
Стили в гнездование
Стили применить метод (ы)
className (используя @emotion/css)styled компонентcss PropСтили вывод
.css извлечение файла<style> Инъекция тега css PROP предлагает отличную эргономику во время разработки, однако, кажется, это новый подход, основанный на новом преобразовании jsx React 17, и настройка его не является тривиальной, отличается от вашей настройки и подразумевает некоторую пакет (которая должна скоро измениться и стать проще) styled подхода добавит 3 kB в наш пакет, потому что он импортируется из отдельного пакетаcss Prop & styled ) Общая эмоция выглядит очень солидным и гибким подходом. Новый подход css PROP предлагает отличную эргономику для разработчиков. Работа с динамическими стилями и типографией довольно простой и интуитивно понятной. Поддерживая как strings , так и objects при определении стилей, его можно легко использовать как при миграции из простых CSS, либо начнется с нуля. Накладные расходы на пакет не незначительны, но определенно намного меньше, чем другие решения, особенно если вы рассматриваете богатый набор функций, которые он предлагает.
Кажется, это не имеет особого внимания на производительности, но больше на опыте разработчиков. Это выглядит как идеальное «хорошо окружающее» решение.
| Передается / gzipped | Несомненно | |
|---|---|---|
| Индексный размер страницы | 83,2 КБ | 253 КБ |
| vs. CSS Modules | +6.5 kB | +20 kB |
Page Size First Load JS
┌ ○ / 2.5 kB 76.4 kB
├ /_app 0 B 73.9 kB
├ ○ /404 194 B 74.1 kB
└ ○ /other 1.07 kB 74.9 kB
+ First Load JS shared by all 73.9 kB
├ chunks/framework.895f06.js 42 kB
├ chunks/main.6cb893.js 23.3 kB
├ chunks/pages/_app.b6d380.js 7.68 kB
└ chunks/webpack.ddd010.js 822 B
Minimal library, focused only on type-checking. It is framework agnostic, that's why it doesn't have a special API for handling dynamic styles. There are React wrappers available, but the typings feels a bit convoluted.
Version: 2.1 | Maintained by Basarat | Launched in 2017 | View Docs | ... back to Overview
✅ Styles/Component co-location
✅ TypeScript support
✅ Context-aware code completion
✅ Framework agnostic
? Built-in Theming support : uses TS namespaces to define theming, which is not a recommended TS feature even by the author himself, or by TS core team member Orta Therox.
No Atomic CSS
Styles definition method(s)
Styles nesting
Styles apply method(s)
classNamestyled componentcss propStyles output
.css file extraction<style> tag injection <style> tag with all the styles, and replaces it on update, and apparently it doesn't use insertRule() , not even in production builds, which might be an important performance drawback in large & highly dynamic UIs Overall TypeStyle seems a minimal library, relatively easy to adopt because we don't have to rewrite our components, thanks to the classic className approach. However we do have to rewrite our styles, because of the Style Object syntax. We didn't feel like writting CSS, so there is a learning curve we need to climb.
With Next.js or React in general we don't get much value out-of-the-box, so we still need to perform a lot of manual work. The external react-typestyle binding doesn't support hooks, it seems to be an abandoned project and the typings are too convoluted to be considered an elegant solution.
| Transferred / gzipped | Uncompressed | |
|---|---|---|
| Index page size | 78.8 kB | 241 kB |
| vs. CSS Modules | +2.1 kB | +8 kB |
Page Size First Load JS
┌ ○ / 2.44 kB 72.1 kB
├ /_app 0 B 69.7 kB
├ ○ /404 194 B 69.9 kB
└ ○ /other 975 B 70.7 kB
+ First Load JS shared by all 69.7 kB
├ chunks/framework.895f06.js 42 kB
├ chunks/main.b2b078.js 23.1 kB
├ chunks/pages/_app.5b0422.js 3.81 kB
└ chunks/webpack.61f1b6.js 778 B
It appears to be a mature solution, with quite a number of users. The API is intuitive and very easy to use, great integration for React using hooks.
Version: 11.6 | Maintained by Robin Weser | Launched in 2016 | View Docs | ... back to Overview
✅ Styles/Component co-location
✅ Built-in Theming support
✅ Atomic CSS
✅ Framework agnostic
? TypeScript support : it exposes Flow types, which work ok, from our (limited) experience
? Context-aware code completion : styles defined outside the component require explicit typing to get code completion
Styles definition method(s)
Styles nesting
Styles apply method(s)
classNamestyled componentcss propStyles output
.css file extraction<style> tag injection a , b , ...)Fela looks to be a mature solution, with active development. It introduces 2 great features which we enjoyed a lot. The first one is the basic principle that "Style as a Function of State" which makes working with dynamic styles feel super natural and integrates perfectly with React's mindset. The second is atomic CSS class names, which should potentially scale great when used in large applications.
The lack of TS support however is a bummer, considering we're looking for a fully type-safe solution. Also, the scaling benefits of atomic CSS should be measured against the library bundle size.
| Transferred / gzipped | Uncompressed | |
|---|---|---|
| Index page size | 88.6 kB | 276 kB |
| vs. CSS Modules | +11.9 kB | +43 kB |
Page Size First Load JS
┌ ○ / 2.84 kB 81.7 kB
├ /_app 0 B 78.9 kB
├ ○ /404 194 B 79 kB
└ ○ /other 1.43 kB 80.3 kB
+ First Load JS shared by all 78.9 kB
├ chunks/framework.2191d1.js 42.4 kB
├ chunks/main.b2b078.js 23.1 kB
├ chunks/pages/_app.32bc1d.js 12.6 kB
└ chunks/webpack.ddd010.js 822 B
Very young library, solid, modern and well-thought-out solution. The overall experience is just great, full TS support, a lot of other useful features baked in the lib.
Version: 0.2.5 (beta) | Maintained by Modulz | Launched in 2020 | View Docs | ... back to Overview
✅ Styles/Component co-location
✅ TypeScript support
✅ Context-aware code completion
✅ Built-in Theming support
✅ Framework agnostic : (available with @stitches/core )
Atomic CSS
Styles definition method(s)
Styles nesting
Styles apply method(s)
classNamestyled componentcss prop (used only to override styled components)Styles output
.css file extraction<style> tag injection variants (for predefined styles), or styles created inside the component to get access to the propsStitches is probably the most modern solution to this date, with full out-of-the-box support for TS. Without a doubt, they took some of the best features from other solutions and put them together for an awesome development experience. The first thing that impressed us was definitely the documentation. The second, is the API they expose which is close to top-notch. The features they provide are not huge in quantity, but are very well-thought-out.
However, we cannot ignore the fact that it's still in beta. Also, the authors identify it as "near-zero runtime" , but at +9 kB gzipped it's debatable.
| Transferred / gzipped | Uncompressed | |
|---|---|---|
| Index page size | 82.0 kB | 250 kB |
| vs. CSS Modules | +5.3 kB | +17 kB |
Page Size First Load JS
┌ ○ / 2.43 kB 75.2 kB
├ /_app 0 B 72.8 kB
├ ○ /404 194 B 73 kB
└ ○ /other 984 B 73.8 kB
+ First Load JS shared by all 72.8 kB
├ chunks/framework.895f06.js 42 kB
├ chunks/main.b2b078.js 23.1 kB
├ chunks/pages/_app.ff82f0.js 6.93 kB
└ chunks/webpack.61f1b6.js 778 B
Probably the grandaddy around here, JSS is a very mature solution being the first of them, and still being maintained. The API is intuitive and very easy to use, great integration for React using hooks.
Version: 10.7 | Maintained by Oleg Isonen and others | Launched in 2014 | View Docs | ... back to Overview
✅ Styles/Component co-location
✅ Built-in Theming support
✅ Framework agnostic
✅ TypeScript support
✅ Context-aware code completion
No Atomic CSS
Styles definition method(s)
Styles nesting
Styles apply method(s)
classNamestyled component (available with additional plugin)css propStyles output
.css file extraction<style> tag injection react-jss package, which is used with React/Next.js, depends on jss-preset-default, which includes many plugins by default, so you don't need to manually add some of the plugins;react-jss uses className by default. There's also styled-jss that uses Styled Components approach, but it has no types, and couldn't make it work on top of react-jss ;injectSheet API (or we couldn't find it anywhere);The API is similar in many ways to React Native StyleSheets, while the hooks helper allows for easy dynamic styles definition. There are many plugins that can add a lot of features to the core functionality, but attention must be payed to the total bundle size, which is significant even with the bare minimum only.
Also, being the first CSS-in-JS solution built, it lacks many of the modern features that focuses on developer experience.
| Transferred / gzipped | Uncompressed | |
|---|---|---|
| Index page size | 94.9 kB | 293 kB |
| vs. CSS Modules | +18.2 kB | +60 kB |
Page Size First Load JS
┌ ○ / 2.45 kB 88 kB
├ /_app 0 B 85.6 kB
├ ○ /404 194 B 85.8 kB
└ ○ /other 992 B 86.6 kB
+ First Load JS shared by all 85.6 kB
├ chunks/framework.2191d1.js 42.4 kB
├ chunks/main.b2b078.js 23.1 kB
├ chunks/pages/_app.5f0007.js 19.2 kB
└ chunks/webpack.9c89cc.js 956 B
A very light-weight solution, with a loads of features.
Version: 2.0 | Maintained by Cristian Bote | Launched in 2019 | View Docs | ... back to Overview
✅ Styles/Component co-location
✅ Built-in Theming support
✅ TypeScript support
✅ Context-aware code completion
✅ Framework agnostic
No Atomic CSS
Styles definition method(s)
Styles nesting
Styles apply method(s)
classNamestyled component ( see details below )css prop ( is supported, but requires a separate babel plugin )Styles output
.css file extraction<style> tag injection <style> tag with all the styles, and appends to it on update, and apparently it doesn't use insertRule() , not even in production builds, which might be an important performance drawback in large & highly dynamic UIs Looking at Goober you cannot ask yourself what kind of magic did Cristian Bote do to fit all the features inside this tiny library. It is really mind blowing. It is marketed as being "less than 1KB" , which is not entirely accurate, but still... it's the smallest library we've tested.
| Transferred / gzipped | Uncompressed | |
|---|---|---|
| Index page size | 77.8 kB | 237 kB |
| vs. CSS Modules | +1.1 kB | +4 kB |
Page Size First Load JS
┌ ○ / 2.77 kB 71.1 kB
├ /_app 0 B 68.3 kB
├ ○ /404 194 B 68.5 kB
└ ○ /other 2.39 kB 70.7 kB
+ First Load JS shared by all 68.3 kB
├ chunks/framework.895f06.js 42 kB
├ chunks/main.b2b078.js 23.1 kB
├ chunks/pages/_app.5ee014.js 2.42 kB
└ chunks/webpack.61f1b6.js 778 B
A rather new library, having the huge Atlassian platform supporting and probably using it. Many existing features, even more in development, or planned for development.
Version: 0.6 | Maintained by Atlassian | Launched in 2020 | View Docs | ... back to Overview
✅ Styles/Component co-location
✅ TypeScript support
✅ Context-aware code completion
✅ Atomic CSS
Not Framework agnostic
No Built-in Theming support (at least at the moment, but it is planned)
Styles definition method(s)
Styles nesting
Styles apply method(s)
className (only supported with a custom ClassNames component)styled componentcss propStyles output
.css file extraction (currently under development, will be shipped in 2021)<style> tag injection css prop is seamless and trivial, not requiring any special setup (unlike Emotion) <head> during SSR - instead they are placed right before the element using them in the <body> , which could potentially provide slightly faster Paint metrics, such as FCP, or LCP, because the browser can start rendering the body faster and incrementally, not waiting for the entire block of styles to be parsedClassNames API, which enables us to apply styles as class name strings, is a bit convoluted and weird at first sight. Compiled is a very promising library. Considering that it offers both atomic CSS, and it plans to support static .css extraction, with excellent TypeScript support and style co-location, it would be quite unique (having only style9 as a direct competitor).
Also, we cannot ignore that is has Atlassian supporting its development, which puts a (slightly) bigger weight on the confidence level.
The total bundle overhead is pretty small, the runtime library being quite light-weight. With static .css file extraction, this could potentially become even smaller.
| Transferred / gzipped | Uncompressed | |
|---|---|---|
| Index page size | 80.2 kB | 242 kB |
| vs. CSS Modules | +3.5 kB | +9 kB |
Page Size First Load JS
┌ ○ / 2.11 kB 71.8 kB
├ /_app 0 B 66.5 kB
├ ○ /404 194 B 66.7 kB
└ ○ /other 888 B 70.6 kB
+ First Load JS shared by all 66.5 kB
├ chunks/framework.895f06.js 42 kB
├ chunks/main.b2b078.js 23.1 kB
├ chunks/pages/_app.ebe095.js 576 B
├ chunks/webpack.ddd010.js 822 B
└ css/a92bf2d3acbab964f6ac.css 319 B
Linaria is all about static CSS extraction and avoiding any runtime overhead.
Version: 3.0 (beta) | Maintained by Callstack | Launched in 2018 | View Docs | ... back to Overview
✅ Styles/Component co-location
✅ TypeScript support
✅ Context-aware code completion
✅ Framework agnostic
✅ Built-in Theming support
No Atomic CSS
Styles definition method(s)
Styles nesting
Styles apply method(s)
classNamestyled componentcss propStyles output
.css file extraction<style> tag injection Linaria is highly inspired from Astroturf, combining various features from other libraries.
Version 3 is currently in Beta, not sure what the changelog is compared to v2. It's still in development by the React/Native geeks at Callstack.io , but we couldn't find which of the big players use it in production.
It seems to have a slightly larger overall page size ( 2.9 KB ), but we didn't investigate where does this come from. Also, there's an open question if this overhead is fixed or if it scales.
PS: thanks to Daniil Petrov for his PR with the Next.js integration
| Transferred / gzipped | Uncompressed | |
|---|---|---|
| Index page size | 79.4 kB | 239 kB |
| vs. CSS Modules | +2.7 kB | +6 kB |
Page Size First Load JS
┌ ○ / 4.99 kB 71.5 kB
├ └ css/16f3e95ede28dcc048f2.css 423 B
├ /_app 0 B 66.5 kB
├ ○ /404 194 B 66.7 kB
└ ○ /other 3.59 kB 70.1 kB
└ css/3064299bff08067ec7dd.css 427 B
+ First Load JS shared by all 66.5 kB
├ chunks/framework.895f06.js 42 kB
├ chunks/main.b2b078.js 23.1 kB
├ chunks/pages/_app.98e8c3.js 598 B
├ chunks/webpack.ddd010.js 822 B
└ css/7739287c04a618ea0c54.css 295 B
Modern solution with great TypeScript integration and no runtime overhead. It's pretty minimal in its features, straightforward and opinionated. Everything is processed at compile time, and it generates static CSS files. Successor of Treat, also be called "Treat v3", is developed and maintained by the same authors.
Version: 1.2 | Maintained by Seek OSS | Launched in 2021 | View Docs | ... back to Overview
✅ TypeScript support
✅ Built-in Theming support
✅ Context-aware code completion
✅ Framework agnostic
? Atomic CSS : can be achieved with Sprinkles
No Styles/Component co-location : styles must be placed in an external .css.ts file
Styles definition method(s)
Styles nesting
Styles apply method(s)
classNamestyled componentcss propStyles output
.css file extraction<style> tag injection & > span ), which might be seen as a downside, but it actually discourages bad-practices like specificity wars , which should be avoided when scaling CSS (however, this is impossible to be statically type-checked without pattern matching , so it will throw a runtime exception)variants based on predefined types, or inline styles for user-defined styles We felt a lot like using CSS Modules: we need an external file for styles, we place the styles on the elements using className , we handle dynamic styles with inline styles , etc. However, we don't write CSS, and the overall experience with TypeScript support is magnificent, because everything is typed, so we don't do any copy-paste . Error messages are very helpful in guiding us when we do something we're not supposed to do.
vanilla-extract is built with restrictions in mind, with a strong user-centric focus, balacing the developer experience with solid TypeScript support. It's also worth mentioning that Mark Dalgleish, co-author of CSS Modules, works at Seek and he's also a contributor.
The authors vision is to think of vanilla-extract as a low-level utility for building higher-level frameworks, which will probably happen in the future.
| Transferred / gzipped | Uncompressed | |
|---|---|---|
| Index page size | 76.7 kB | 231 kB |
| vs. CSS Modules | +0.0 kB | -2 kB |
Page Size First Load JS
┌ ○ / 2.09 kB 68.5 kB
├ └ css/37c023369f5e1762e423.css 370 B
├ /_app 0 B 66.4 kB
├ ○ /404 194 B 66.6 kB
└ ○ /other 611 B 67 kB
└ css/a56b9d05c6da35ff125f.css 386 B
+ First Load JS shared by all 66.4 kB
├ chunks/framework.895f06.js 42 kB
├ chunks/main.700159.js 23.1 kB
├ chunks/pages/_app.bfd136.js 565 B
├ chunks/webpack.61f1b6.js 778 B
└ css/23b89d9ef0ca05e4b917.css 286 B
We know there are a lot of other libraries out there, besides the ones covered above. We're only covered the ones that have support for React , support for SSR , an easy integration with Next.js , good documentation and a sense of ongoing support and maintenance . Please checkout our goals.
Treat was initially included in the analysis with v1.6, but removed for a few reasons:
The main difference between vanilla-extract and Treat is that the latter supports IE and legacy browsers as well.
Style9 is a new library, inspired by Facebook's own CSS-in-JS solution called stylex. Style9 is unique because it's the only open source library that supports both .css static extraction + atomic CSS, and/or styles co-location. It has TS support and easy to integrate with Next.js.
However, it has quite a few limitations (at least as of Feb 2021) that makes it practically unusable in a real production application that we would want to scale, both in code & team size:
Enum or POJO , only constant primitives are supported, which is a big deal breaker ;classNames lib, but not dynamically/computed/expression based;Some upsides:
As a conclusion, it wants to be a powerful solution with very interesting and unique set of features, but it's not mature yet. As far as we see, it's currently mostly designed towards more static solutions. Dynamic styling seems to be difficult to handle, at least for the moment.
Not an actual CSS-in-JS library, more like a replacement for traditional CSS styling. It uses atomic CSS classes (some of them having multiple properties) that we attach to html elements. We don't write CSS, instead we use a different DSL to specify styles, pseudo classes, media queries, etc.
The reason we didn't include it in our thorough review is because it doesn't fully meet our goals:
.ts files to include them in tailwind.config (cannot import any file, cannot require .ts )tailwind.config directly offers no type-safety when importing it, or using resolveConfigrounded , place-self/content , divide , ring )::after pseudo elements are trickySome upsides:
tailwind.configTailwind seems to be more than a styling tool , it also offers some out-of-the-box utils + a ready-made design system that you can use right away.
It's not a popular solution, the approach is similar to React Native StyleSheets way of styling components. Has built-in TypeScript support and a simple API.
I got it started with Next.js, but it feels fragile. The Glamor official example throws an error regarding rehydrate . When commenting it out, it works, but not sure what the consequences are.
Didn't manage to start it with Next.js + TypeScript. The official example uses version 3, while today we have version 6. The example doesn't work, because the API has changed.
The solution looked interesting, because it is supposed to be very light-weight.
Didn't manage to start it with Next.js + TypeScript. There was an official example that used an older version of Next.js, but the example if not there anymore.
The solution is not that popular, but it was the first to use .css extraction with collocated styles.
Looks promising, atomic css and light-weight. It has a working Next.js example, but we didn't consider it because it lacks any documentation.
It looks like a not so popular solution, which also lacks support for TypeScript. It looks like the maintainers work at Uber and they use it internally. It focused on generating unique atomic CSS classes, which could potentially deduplicate a lot of code.
The project was put in Maintenance Mode. They recommend other solutions.
The project was discontinued in favor of Emotion.
Each implementation sits on their own branch, so we can have a clear separation at built time.
# install dependencies
yarn
# for development
yarn dev
# for production
yarn build
yarn startTo get in touch, my DMs are open @pfeiffer_andrei.
Special thanks and appreciations go to everyone that helped putting this document together, and making it more accurate: