Ce document contient une analyse approfondie de toutes les solutions CSS-in-JS actuelles, qui prennent en charge le rendu et les dactylographiques du serveur.
La référence de base que nous utiliserons pour la comparaison est une approche de modules CSS .
Nous utilisons Next.js comme cadre SSR pour la construction de ressources.
Le dernier aspect important est la sécurité de type avec une prise en charge complète de typeScript .
? Dernière mise à jour: août 2021
? Pour obtenir un aperçu plus court, vous pouvez vérifier l' article sur les astuces CSS :
https://css-tricks.com/a-thorough-analysis-of-css-in-js/
? Si vous préférez une vidéo à la place, vous pouvez vérifier mon discours à partir de Ngpartycz :
https://www.youtube.com/watch?v=C7UWGHRAX9A
Veuillez vérifier nos objectifs et nos clauses de non-responsabilité avant de sauter aux conclusions.
Les modules CSS Language et CSS ont certaines limites, surtout si nous voulons avoir du code de sécurité. Certaines de ces limitations ont des solutions alternatives, d'autres sont tout simplement ennuyeuses ou moins qu'idéales :
Les styles ne peuvent pas être colocalisés avec des composants
Cela peut être frustrant lors de la création de nombreux petits composants, mais ce n'est pas une rupture. Cependant, l'expérience de déplacement des allers-retours entre le fichier component.js et le fichier component.css , recherche d'un nom de classe donné, et ne pas pouvoir «aller à la définition de style» , est un inconvénient important de la productivité.
Les pseudos et les requêtes multimédias de style nécessitent la duplication du sélecteur
Un autre fait frustrant est la nécessité de dupliquer nos cours CSS lors de la définition des cours et des éléments pseudo , ou des requêtes multimédias . Nous pouvons surmonter ces limitations à l'aide d'un préprocesseur CSS comme Sass, moins ou stylet , qui prend en charge le sélecteur & parent, permettant un style contextuel .
. button {}
/* duplicated selector declaration for pseudo classes/elements */
. button : hover {}
. button :: after {}
@media ( min-width : 640 px ) {
/* duplicated selector declaration inside media queries */
. button {}
} L'utilisation des styles est déconnectée de leur définition
Nous n'obtenons aucune intelligence avec les modules CSS, de ce que les classes CSS sont définies dans le fichier component.css , faisant de la copie-coller un outil requis, abaissant le DX. Il rend également le refactoring très lourd , en raison du manque de sécurité.
L'utilisation de jetons de conception de type sécurisé dans CSS est non trivial
Tous les jetons de conception définis dans JS / TS (pour bénéficier de la sécurité de type) ne peuvent pas être directement utilisés dans CSS.
Il y a au moins 2 solutions de contournement pour ce problème, aucune des deux n'est élégante:
.module.css ..css .Il y a des objectifs spécifiques que nous recherchons avec cette analyse:
Devenant encore plus précis, nous voulions vivre l'utilisation de diverses solutions CSS-in-JS concernant:
props de composants (aka.Cette analyse est destinée à être objective et non opinée :
? Qu'est-ce que vous ne trouverez pas ici?
? Qu'est-ce que vous trouverez ici?
Les bibliothèques ne sont présentées dans aucun ordre particulier. Si vous êtes intéressé par un bref historique de CSS-in-JS , vous devriez vérifier le passé, le présent et l'avenir de CSS-in-JS Insightful Talk de Max Stoiber.
| 1. Co-emplacement | 2. Dx | 3. tag` ` | 4. { } | 5. TS | 6. & ctx | 7. Nesting | 8. Thème | 9. .css | 10. <style> | 11. Atomique | 12. className | 13. <Styled /> | 14. css Prop | 15. Agnostic | 16. Taille de la page Delta | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Modules CSS | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | - | |||||||||
| Styled JSX | ✅ | ? | ✅ | ? | ✅ | ✅ | ✅ | +2.8 kB / +12.0 kB | ||||||||
| Composants stylisés | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +13.4 kB / +39.0 kB | ||||
| Émotion | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +6.5 kB / +20.0 kB | ||
| Type de type | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | ✅ | +2.1 kB / +8.0 kB | |||||
| Fela | ✅ | ? | ? | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +11.9 kB / +43.0 kB | |||
| Suture | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +5.3 kB / +17.0 kB | |||
| JSS | ✅ | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +18.2 kB / +60.0 kB | |||
| Goober | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +1.1 kB / +4.0 kB | ||
| Compilé | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | +3.5 kB / +9.0 kB | |||
| Linaria | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +2.7 kB / +6.0 kB | ||||
| vanilla | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | +0.0 kB / -2.0 kB |
La possibilité de définir des styles dans le même fichier que le composant. Notez que nous pouvons également extraire les styles dans un fichier séparé et les importer, au cas où nous le préférerons.
⬆️ pour un aperçu
Fait référence à l' expérience du développeur qui comprend 2 aspects principaux:
⬆️ pour un aperçu
tag` ` (modèles tagués)Prise en charge des styles de définition comme des chaînes , en utilisant des modèles étiquetés ES:
kebab-case pour les noms de propriétés, tout comme la syntaxe CSS simple;string simple;⬆️ pour un aperçu
{ } (styles d'objet)Prise en charge de la définition des styles comme objets , en utilisant des objets JavaScript simples:
camelCase pour les noms de propriétés, comme nous le ferions dans React Native;⬆️ pour un aperçu
Prise en charge de dactylographie, soit intégrée, soit via le package @types , qui devrait inclure:
Props génériques, le cas échéant (obtenir un accès à sécurité aux types d'accessoires de composants lors de la définition des styles dynamiques);⬆️ pour un aperçu
& ctx (styles contextuels)Prise en charge des styles contextuels nous permettant de définir facilement des classes et des éléments pseudo et des requêtes multimédias sans avoir besoin de répéter le sélecteur, comme requis dans CSS ordinaire:
& parent;⬆️ pour un aperçu
Prise en charge des sélecteurs imbriqués arbitraires :
⬆️ pour un aperçu
Prise en charge intégrée pour le thème ou la gestion des jetons pour un système de conception.
Nous n'avons pas testé cette fonctionnalité , nous prenons donc seulement des notes quelles bibliothèques expriment leur soutien dans leurs documents.
⬆️ pour un aperçu
.css (Extraction CSS statique) Les styles définis sont extraits comme des fichiers .css statiques:
⬆️ pour un aperçu
<style> Tag Les styles définis sont injectés à l'intérieur des balises <style> dans le <head> du document::
⬆️ pour un aperçu
La capacité de générer des classes CSS atomiques , augmentant ainsi la réutilisabilité du style et réduisant la duplication:
⬆️ pour un aperçu
className L'API de la bibliothèque renvoie une string que nous devons ajouter à notre composant ou à notre élément;
⬆️ pour un aperçu
<Styled /> L'API crée un composant wrapper (ou Styled ) qui comprend le (s) className généré:
StyledButton ou StyledList au lieu de constantes comme button_styles ou list_styles , donc en ce qui concerne le nom, c'est à peu près la même chose;⬆️ pour un aperçu
css Prop Permet de passer des styles à l'aide d'un accessoire css spécial, similaire comment nous définissons les styles en ligne, mais la bibliothèque génère un nom de classe CSS unique dans les coulisses:
⬆️ pour un aperçu
Permet l'utilisation sans, ou avec n'importe quel cadre. Certaines bibliothèques sont construites spécifiquement pour React uniquement.
Remarque : Certaines bibliothèques comme les points de suture ou le document d'émotion ne réagissent que l'utilisation, bien qu'elles aient un noyau qui est agnostique.
⬆️ pour un aperçu
La différence totale de taille de page dans KB (transféré gzipped & minifié / non compressé et minifié) par rapport aux modules CSS , pour toute la construction de production de page d'index en utilisant Next.js:
Remarque: Toutes les versions ont été effectuées avec Next.js 11.1.0 et les valeurs sont tirées de l'onglet Chrome Devtools Network, transféré sur la taille du réseau vs des ressources.
⬆️ pour un aperçu
Les observations suivantes s'appliquent à toutes les solutions (avec des exceptions pointues mineures).
Les composants utilisés uniquement dans une route spécifique ne seront regroupés que pour cette route. C'est quelque chose que Next.js effectue des performances hors de la boîte.
Toutes les solutions offrent un moyen de définir les styles mondiaux, certains avec une API dédiée.
Toutes les solutions offrent une prise en charge du rendu côté serveur et sont faciles à intégrer avec Next.js.
Toutes les solutions ajoutent automatiquement des préfixes spécifiques aux fournisseurs prêts à l'emploi.
Toutes les solutions génèrent des noms de classe uniques, comme le font les modules CSS. L'algorithme utilisé pour générer ces noms varie beaucoup entre les bibliothèques:
.heading du composant Card aura toujours le hash .Card_heading_h7Ys5 );.heading-0-2-1 , .input-0-2-2 ), soit les lettres d'alphabet ( a, b, c, ... aa, ab, ac , etc.), ce qui rend cette approche plus performante, mais entraînant des noms de classe non compatibles (ne peut pas déterminer si cela a des étalages potentiels ou non); Aucune des solutions ne génère des styles en ligne, qui est une approche plus ancienne, utilisée par le radium et le glamour. L'approche est moins performante que les classes CSS, et elle n'est pas recommandée comme méthode principale pour définir les styles. Cela implique également d'utiliser des gestionnaires d'événements JS pour déclencher des classes pseudo, car les styles en ligne ne les soutiennent pas. Apparemment, toutes les solutions modernes se sont éloignées de cette approche.
Toutes les solutions prennent en charge la plupart des propriétés CSS dont vous auriez besoin: les classes et éléments pseudo , les requêtes multimédias et les images clés sont celles que nous avons testées.
La plupart des solutions se sont commercialisées comme étant capables de "extraire les CSS critiques" pendant la RSS. Veuillez noter que cela ne fait pas référence à l'extraction CSS critique au-dessus du fois, comme nous le pensons initialement.
Ce qu'ils font réellement:
Avec CSS 100% statique, il n'y aurait en fait aucun avantage. Avec des pages dynamiques qui rendent très peu d'éléments sur le serveur, et la plupart des composants sont rendus dynamiquement sur le client, les avantages augmentent.
Exception : les bibliothèques qui utilisent l'extraction CSS statique.
Comprendre comment ces fonctionnalités affectent les vitaux Web de base et les métriques de performance en général est un facteur extrêmement important à considérer, et la façon dont les styles sont livrés au client ont probablement le plus grand impact, alors analysons cela en détail.
De plus, il y a 2 scénarios différents que nous devons considérer:
.js , .css , médias, etc.); .css Les solutions qui génèrent des fichiers statiques .css , que vous incluriez normalement en tant que <link> tag (s) dans le <head> de votre page, rendent essentiellement des ressources de blocage. Cela affecte fortement le FCP , le LCP et toute autre métrique qui suit.
? Cache vide
Si l'utilisateur a un cache vide, ce qui suit doit se produire, impactant négativement FCP et LCP :
<body> , même si le HTML entier est déjà chargé, et il peut même être analysé avec impatience, et certaines ressources déjà approchées à l'avance; Il est vrai que vous pouvez récupérer dans d'autres ressources <head> parallèles (fichiers .css ou .js supplémentaires), mais c'est généralement une mauvaise pratique;
? Cache complet
Cependant, lors des visites ultérieures, la ressource .css entière serait mise en cache, donc FCP et LCP seraient impactés positivement.
Points clés
Cette solution semble mieux adaptée lorsque:
.css commun qui peut être mis en cache lors de la visite d'autres pages;<style> Styles injectés de balise Pendant SSR , les styles seront ajoutés en tant que <style> TAG (s) dans la <head> de la page. Gardez à l'esprit que ceux-ci n'incluent généralement pas tous les styles nécessaires à la page, car la plupart des bibliothèques effectuent une extraction CSS critique, de sorte que ces styles doivent être généralement plus petits que l'ensemble du fichier statique .css discuté précédemment.
? Cache vide
Parce que nous expédions moins d'octets CSS et qu'ils sont inclus dans le fichier .html , cela entraînerait un FCP et LCP plus rapides:
.css , de sorte que le navigateur n'est pas bloqué;.js à la fin du document, <head> ne fera aucune demande, donc le rendu se produira super rapide;.css :.js avec les composants, pendant l'hydratation (cela inclut tous les CS critiques déjà expédiés à l'intérieur du <style> tag + autres); ? Cache complet
Lorsque le cache de l'utilisateur est plein, les fichiers .js supplémentaires ne nécessiteront pas de récupération, car ils sont déjà mis en cache.
Cependant, si la page est SSRED , le CSS critique en ligne rendue dans la balise <style> du document sera à nouveau téléchargé, sauf si nous traitons avec un HTML statique qui peut également être mis en cache, ou nous traitons la mise en cache HTML sur notre infrastructure.
Mais, par défaut, nous expédierons des octets supplémentaires sur chaque demande HTTP, qu'elle soit mise en cache ou non.
Points clés
Cette solution semble mieux adaptée lorsque:
La plupart des solutions disent qu'ils suppriment des code / styles inutilisés . Ce n'est que de la moitié .
Le code inutilisé est en effet plus difficile à accumuler, surtout si vous le comparez à des fichiers .css simples comme nous le faisions il y a une décennie . Mais par rapport aux modules CSS, les différences ne sont pas si grandes. Toute solution qui offre la possibilité de définir des sélecteurs arbitraires ou des styles imbriqués les regroupera, qu'ils soient utilisés ou non dans notre composant. Nous avons réussi à expédier des styles SSR inutilisés avec toutes les solutions testées.
La suppression de code non utilisée True & Full est difficile à mettre en œuvre, car la syntaxe CSS n'est pas vérifiée par type, ni analysée statiquement. De plus, la nature dynamique des composants le rend pratiquement impossible dans certains scénarios, en particulier lorsque le balisage est rendu dynamiquement:
& span : Éléments descendant;&:nth-child() : Certains sélecteurs de pseudo;& .bg-${color} : sélecteurs dynamiques;.parent & : Sélecteurs parents;Fondamentalement, ce que nous obtenons, c'est la suppression de code lorsque nous supprimons le composant, ou nous ne l'importons plus. C'est un comportement implicite, car les styles sont une dépendance directe du composant. Lorsque le composant est parti, ses styles aussi.
Il existe 2 méthodes pour injecter CSS dans le DOM et la mettre à jour à partir de JavaScript:
<style> Cette approche implique d'ajouter une ou plusieurs balises <style> dans le DOM (dans le <head> ou quelque part dans le <body> ), en utilisant .APPENDCHILD () pour ajouter le (s) <style> nœud (s <style> , en plus avec.
<style> et de mise à jour de son contenu entier pourrait être lente pour mettre à jour l'intégralité du DOM lorsque nous avons réellement modifié un minuscule ensemble de règles CSS;DEVELOPMENT , car elle offre une meilleure expérience de débogage;PRODUCTION également; CSSStyleSheet Utilisé pour la première fois par JSS , cette méthode utilise CSSStyleSheet.insertRule() pour injecter des règles CSS directement dans le CSSOM .
<style> vide;<style> ;$0 dans Chrome Devtools (ou obtenez une référence à lui d'une autre manière, en utilisant l'API DOM);.sheet.cssRules sur la balise <style> pour voir le tableau des règles CSS qu'il contient;PRODUCTION ;DEVELOPMENT ; Si le même composant est importé par 2 itinéraires différents, il sera envoyé deux fois au client. Il s'agit sûrement d'une limitation du système de bundler / build, dans notre cas suivant.js, et non lié à la solution CSS-in-JS .
Dans Next.js, le coup de code fonctionne au niveau de l'itinéraire, regroupant tous les composants requis pour un itinéraire spécifique, mais selon leur blog officiel et web.dev si un composant est utilisé dans plus de 50% des pages, il devrait être inclus dans le bundle commons . Cependant, dans notre exemple, nous avons 2 pages, chacune d'eux important le composant Button , et elle est incluse dans chaque bundle de pages, pas dans le bundle commons . Étant donné que le code requis pour le style est emballé avec le composant, cette limitation aura également un impact sur les styles, il vaut donc la peine de garder cela à l'esprit.
Il s'agit d'une approche bien établie, mature et solide. Sans aucun doute, c'est une grande amélioration par rapport à BEM, SMACCS, OOCSS ou toute autre méthodologie CSS évolutive pour structurer et organiser notre CSS, en particulier dans les applications basées sur les composants.
Lancé en 2015 | Retour à la vue d'ensemble
✅ Achèvement du code au contexte
✅ Framework Agnostic
Aucun styles / colocalisation des composants
Aucune prise en charge dactylographiée
Pas de CSS atomique
Aucun support de thème
Méthode de définition des styles (s)
Styles niche
Styles Appliquer la ou les méthodes (s)
classNamestyledcssSortie des styles
.css<style> Injection de tagC'est la ligne de base que nous considérerons lors de la comparaison de toutes les solutions CSS-in-JS suivantes. Découvrez la motivation pour mieux comprendre les limites de cette approche que nous essayons de combler.
| Transféré / gzipped | Non compressé | |
|---|---|---|
| Taille de la page d'index | 76,7 Ko | 233 Ko |
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
Solution très simple, n'a pas de site Web dédié pour la documentation, tout est sur GitHub. Ce n'est pas populaire, mais c'est la solution intégrée dans Next.js.
Version: 4.0 | Maintenu par Vercel | Lancé en 2017 | Afficher les documents | ... Retour à la vue d'ensemble
✅ Styles / Co-location des composants
? Achèvement du code contextuel : Pour obtenir la mise en surbrillance de la syntaxe et l'achèvement du code, une extension de l'éditeur est requise
? Prise en charge de la type : @types peut être entièrement installé, mais l'API est trop minime pour exiger TS
Pas de CSS atomique
Aucun support de thème
Pas le cadre agnostique
Méthode de définition des styles (s)
Styles niche
Styles Appliquer la ou les méthodes (s)
classNamestyledcssSortie des styles
.css<style> Injection de tag elements HTML, et il génère des noms de classe uniques pour eux (je ne sais pas si c'est une bonne pratique, cependant) Dans l'ensemble, nous avions envie d'écrire des CSS simples, avec l'avantage supplémentaire de pouvoir définir les styles avec le composant, nous n'avons donc pas besoin d'un fichier .css supplémentaire . En effet, c'est la philosophie de la bibliothèque: soutenir la syntaxe CSS dans le fichier composant. Nous pouvons utiliser toutes les constantes JS / TS de fonctions avec interpolation de chaîne. Travailler avec des styles dynamiques est assez facile car il est fini par JavaScript. Nous obtenons tous ces avantages à un prix très bas, avec un joli paquet aérien .
Les inconvénients sont l'expérience globale de la création de CSS simples. Sans support de nidification Pseudo Classes / éléments et requêtes multimédias devenir assez lourdes à gérer.
| Transféré / gzipped | Non compressé | |
|---|---|---|
| Taille de la page d'index | 79,5 Ko | 245 Ko |
| Vs modules CSS | +2,8 Ko | +12 kb |
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
Bien sûr, l'une des solutions les plus populaires et les plus matures, avec une bonne documentation. Il utilise des modèles marqués pour définir les styles par défaut, mais peut également utiliser des objets. Il a également popularisé l'approche des composants styled , qui crée un nouveau composant avec les styles définis.
Version: 5.3 | Entretenu par Max Stoiber et autres | Lancé en 2016 | Afficher les documents | ... Retour à la vue d'ensemble
✅ Styles / Co-location des composants
✅ TypeScript Support : @types doit être entièrement installé, via définitivement Typed
✅ Prise en charge du thème intégré
✅ Framework Agnostic
? Achèvement du code contextuel : nécessite une extension / plugin d'éditeur
Pas de CSS atomique
Méthode de définition des styles (s)
Styles niche
Styles Appliquer la ou les méthodes (s)
classNamestyledcss PropSortie des styles
.css<style> Injection de tag Props , etc.) Styled Components propose une nouvelle approche des composants de style utilisant la méthode styled qui crée un nouveau composant comprenant les styles définis. Nous n'avons pas envie d'écrire CSS, donc en provenance de modules CSS, nous devrons apprendre un nouveau moyen plus programmatique, pour définir les styles. Parce qu'il permet à la fois la syntaxe string et object , c'est une solution assez flexibile à la fois pour migrer nos styles existants et pour démarrer un projet à partir de zéro. En outre, les mainteneurs ont fait un très bon travail en suivant la plupart des innovations dans ce domaine.
Cependant, avant de l'adopter, nous devons être conscients qu'il comporte un certain coût pour notre taille de bundle.
| Transféré / gzipped | Non compressé | |
|---|---|---|
| Taille de la page d'index | 90,1 Ko | 272 Ko |
| Vs modules CSS | +13.4 Ko | +39 kb |
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
Probablement la solution la plus complète, complète et Sofistique. La documentation détaillée, entièrement construite avec dactylographie, semble très mature, riche en fonctionnalités et bien entretenue.
Version: 11.4 | Entretenu par Mitchell Hamilton et autres | Lancé en 2017 | Afficher les documents | ... Retour à la vue d'ensemble
✅ Styles / Co-location des composants
✅ Prise en charge des dactylographiques
✅ Prise en charge du thème intégré
✅ Achèvement du code au contexte : Pour utiliser l'approche des composants styled , un plugin supplémentaire d'éditeur est requis
✅ Framework Agnostic
Pas de CSS atomique
Méthode de définition des styles (s)
Styles niche
Styles Appliquer la ou les méthodes (s)
className (en utilisant @ émotion / css)styledcss PropSortie des styles
.css<style> Injection de tag css offre une grande ergonomie pendant le développement, mais elle semble être une approche plus récente, basée sur React 17 New jsx Transform, et la configurer n'est pas triviale, diffère votre configuration, et implique un peu de chauffeur (qui devrait changer bientôt et devenir plus facile) styled ajoutera 3 kB à notre paquet, car il est importé d'un package séparécss et les composants styled ) L'émotion globale semble être une approche très solide et flexible. La nouvelle approche css Prop offre une grande ergonomie aux développeurs. Travailler avec des styles dynamiques et dactylographiques est assez facile et intuitif. Soutenir à la fois strings et objects lors de la définition des styles, il peut être facilement utilisé à la fois lors de la migration de CSS ordinaire ou à partir de zéro. Les frais généraux du bundle ne sont pas négligeables, mais certainement beaucoup plus petits que les autres solutions, surtout si vous considérez le riche ensemble de fonctionnalités qu'il propose.
Il semble qu'il ne se concentre pas sur les performances, mais plus sur l'expérience des développeurs. Cela ressemble à une solution parfaite "bien équilibrée".
| Transféré / gzipped | Non compressé | |
|---|---|---|
| Taille de la page d'index | 83,2 Ko | 253 Ko |
| Vs modules CSS | +6,5 Ko | +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
Bibliothèque minimale, axée uniquement sur la vérification du type. 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: