Este documento contiene un análisis en profundidad de todas las soluciones CSS-in-JS actuales, que admiten representación y mecanografiado del lado del servidor.
La referencia de línea de base que usaremos para comparación es un enfoque de módulos CSS .
Estamos utilizando Next.js como marco de SSR para construir recursos.
El último aspecto importante es la seguridad de tipo con soporte de mecanografiado completo.
? Última actualización: agosto de 2021
? Para obtener una descripción general más corta, puede consultar el artículo sobre los trucos de CSS :
https://css-tricks.com/a-thorough-analysis-of-css-in-js/
? Si prefiere un video, puede consultar mi charla desde NgPartycz :
https://www.youtube.com/watch?v=c7uwghrax9a
Consulte nuestros objetivos y descargo de responsabilidad antes de llegar a conclusiones.
El lenguaje CSS y los módulos CSS tienen algunas limitaciones, especialmente si queremos tener un código seguro. Algunas de estas limitaciones tienen soluciones alternativas, otras son solo molestas o menos que ideales :
Los estilos no se pueden ubicar con componentes
Esto puede ser frustrante al autorizar muchos componentes pequeños, pero no es un factor decisivo. Sin embargo, la experiencia de moverse de ida y vuelta entre el archivo component.js y el archivo component.css , la búsqueda de un nombre de clase determinado y no poder "ir a la definición de estilo" fácilmente es un inconveniente de productividad importante.
El estilo de pseudos y consultas de medios requiere duplicación selectora
Otro hecho frustrante es la necesidad de duplicar nuestras clases de CSS al definir las clases y elementos de pseudo , o las consultas de los medios . Podemos superar estas limitaciones utilizando un preprocesador CSS como Sass, Less o Stylus , que admite el selector de padres & el estilo contextual .
. button {}
/* duplicated selector declaration for pseudo classes/elements */
. button : hover {}
. button :: after {}
@media ( min-width : 640 px ) {
/* duplicated selector declaration inside media queries */
. button {}
} El uso de estilos se desconecta de su definición
No obtenemos IntelliSense con los módulos CSS, de lo que las clases de CSS se definen en el archivo component.css , haciendo de Copy-Paste una herramienta requerida, bajando el DX. También hace que la refactorización sea muy engorrosa , debido a la falta de seguridad.
El uso de tokens de diseño tipo seguro en CSS no es trivial
Cualquier tokens de diseño definido en JS/TS (para beneficiarse de la seguridad de tipo) no puede usarse directamente en CSS.
Hay al menos 2 soluciones para este problema, ninguno de ellos es elegante:
.module.css ..css .Hay objetivos específicos que estamos buscando con este análisis:
En cuanto a más específicos, queríamos experimentar el uso de varias soluciones CSS-in-JS con respecto a:
props de componentes (también conocido como variantes de componentes) o desde la entrada del usuarioEste análisis está destinado a ser objetivo y no opensado :
? ¿Qué no encontrarás aquí?
? ¿Qué encontrarás aquí?
Las bibliotecas no se presentan en ningún orden en particular. Si está interesado en una breve historia de CSS-in-JS , debe revisar el pasado, el presente y el futuro de CSS-in-JS Persportable Chalk de Max Stoiber.
| 1. Co -ubicación | 2. DX | 3. tag` ` | 4. { } | 5. TS | 6. & CTX | 7. Anidación | 8. Tema | 9 .css | 10. <style> | 11. Atómico | 12. className | 13. <Styled /> | 14. css Prop | 15. Agnóstico | 16. Tamaño de página Delta | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Módulos CSS | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | - | |||||||||
| JSX de estilo | ✅ | ? | ✅ | ? | ✅ | ✅ | ✅ | +2.8 kB / +12.0 kB | ||||||||
| Componentes de estilo | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +13.4 kB / +39.0 kB | ||||
| Emoción | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +6.5 kB / +20.0 kB | ||
| Estilo de típico | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | ✅ | +2.1 kB / +8.0 kB | |||||
| Felas | ✅ | ? | ? | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +11.9 kB / +43.0 kB | |||
| Puntadas | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +5.3 kB / +17.0 kB | |||
| JSS | ✅ | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +18.2 kB / +60.0 kB | |||
| Goober | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +1.1 kB / +4.0 kB | ||
| Compilado | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | +3.5 kB / +9.0 kB | |||
| Linaria | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +2.7 kB / +6.0 kB | ||||
| Extracto de vainilla | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | +0.0 kB / -2.0 kB |
La capacidad de definir estilos dentro del mismo archivo que el componente. Tenga en cuenta que también podemos extraer los estilos en un archivo separado e importarlos, en caso de que lo preferimos.
⬆️ para general
Se refiere a la experiencia del desarrollador que incluye 2 aspectos principales:
⬆️ para general
tag` ` (plantillas etiquetadas)Soporte para definir estilos como cadenas , utilizando plantillas etiquetadas con ES:
kebab-case para nombres de propiedades, al igual que la sintaxis CSS simple;string simple;⬆️ para general
{ } (Estilos de objeto)Soporte para definir estilos como objetos , usando objetos JavaScript simples:
camelCase para nombres de propiedades, como lo haríamos en React Native;⬆️ para general
Soporte de TypeScript, ya sea incorporado o a través del paquete @types , que debe incluir:
Props Generics, cuando corresponda (obtenga acceso a tipo seguro a los tipos de accesorios de componentes al definir estilos dinámicos);⬆️ para general
& CTX (estilos contextuales)Soporte para estilos contextuales que nos permiten definir fácilmente las clases y elementos de pseudo y consultas de medios sin la necesidad de repetir el selector, como se requiere en CSS simple:
& PADRES;⬆️ para general
Soporte para selectores anidados arbitrarios :
⬆️ para general
Soporte incorporado para temas o tokens para un sistema de diseño.
No hemos probado esta función , por lo que solo estamos tomando notas qué bibliotecas expresan su apoyo en sus documentos.
⬆️ para general
.css (extracción estática CSS) Los estilos definidos se extraen como archivos .css estáticos:
⬆️ para general
<style> etiqueta Los estilos definidos se inyectan dentro de las etiquetas <style> en el <head> del documento:
⬆️ para general
La capacidad de generar clases Atomic CSS , aumentando así la reutilización del estilo y reduciendo la duplicación:
⬆️ para general
className La API de la biblioteca devuelve una string que tenemos que agregar a nuestro componente o elemento;
⬆️ para general
<Styled /> La API crea un componente de envoltura (o Styled ) que incluye el className (s) generado (s):
StyledButton o StyledList en lugar de constantes como button_styles o list_styles , por lo que con respecto a nombrar es más o menos lo mismo;⬆️ para general
css Prop Permite pasar estilos utilizando un accesorio css especial, similar a cómo definiríamos los estilos en línea, pero la biblioteca genera un nombre de clase CSS único detrás de escena:
⬆️ para general
Permite el uso sin ni con cualquier marco. Algunas bibliotecas están construidas específicamente para React solamente.
Nota : Algunas bibliotecas como puntadas o documentos de emoción solo reaccionan el uso, aunque tienen un núcleo que es marco agnóstico.
⬆️ para general
La diferencia total de tamaño de la página en KB (transferido GZipped & minificado / sin comprimir y minificado) en comparación con los módulos CSS , para toda la construcción de producción de la página de índice usando Next.js:
Nota: Todas las compilaciones se realizaron con Next.js 11.1.0 y los valores se toman de la pestaña Network Chrome DevTools, transferido a través del tamaño de la red vs del recurso.
⬆️ para general
Se aplican las siguientes observaciones para todas las soluciones (con excepciones puntuales menores).
Los componentes utilizados solo en una ruta específica solo se agruparán para esa ruta. Esto es algo que Next.js realiza fuera de la caja.
Todas las soluciones ofrecen una forma de definir estilos globales, algunos con una API dedicada.
Todas las soluciones ofrecen soporte de representación del lado del servidor y son fáciles de integrar con Next.js.
Todas las soluciones agregan automáticamente prefijos específicos del proveedor fuera de la caja.
Todas las soluciones generan nombres de clase únicos, como lo hacen los módulos CSS. El algoritmo utilizado para generar estos nombres varía mucho entre las bibliotecas:
.heading del componente Card siempre tendrá el hash .Card_heading_h7Ys5 );.heading-0-2-1 , .input-0-2-2 ), o las letras del alfabeto ( a, b, c, ... aa, ab, ac , etc.), lo que hace que este enfoque sea más performador, pero que resulta en nombres de clase no ideampotentes (no se puede encontrar si esto tiene algún posible retiro o no); Ninguna de las soluciones genera estilos en línea, que es un enfoque más antiguo, utilizado por Radium & Glamour. El enfoque es menos desempeñado que las clases de CSS, y no se recomienda como un método principal para definir estilos. También implica el uso de controladores de eventos JS para activar las clases de pseudo, ya que los estilos en línea no los admiten. Aparentemente, todas las soluciones modernas hoy en día se alejaron de este enfoque.
Todas las soluciones admiten la mayoría de las propiedades de CSS que necesitaría: las clases y elementos de pseudo , consultas de medios y fotogramas clave son las que hemos probado.
La mayoría de las soluciones se comercializan en sí mismas como capaces de "extraer CSS crítico" durante la SSR. Tenga en cuenta que esto no se refiere a la extracción CSS crítica anterior, como pensamos inicialmente.
Lo que realmente hacen:
Con CSS 100% estático, en realidad no habría ningún beneficio. Con páginas dinámicas que representan muy pocos elementos en el servidor, y la mayoría de los componentes se representan dinámicamente en el cliente, el beneficio aumenta.
Excepción : bibliotecas que usan extracción CSS estática.
Comprender cómo estas características afectan los vitales de la web núcleo y las métricas de rendimiento en general es un factor extremadamente importante a considerar, y la forma en que los estilos se entregan al cliente probablemente tienen el mayor impacto, así que analicemos esto en detalle.
Además, hay 2 escenarios diferentes que debemos considerar:
.js , .css , medios, etc.); .css Extracción de archivos Las soluciones que generan archivos estáticos .css , que normalmente incluirían como <link> etiquetas en el <head> de su página, son básicamente recursos de bloqueo. Esto afecta altamente FCP , LCP y cualquier otra métrica que siga.
? Caché vacío
Si el usuario tiene un caché vacío, lo siguiente debe suceder, lo que impacta negativamente FCP y LCP :
<body> , incluso si todo el HTML ya está cargado, e incluso puede analizarse ansiosamente, y algunos recursos ya se obtienen de antemano; Es cierto que puede obtener en otros recursos <head> ( archivos .css o .js adicionales), pero esto generalmente es una mala práctica;
? Caché completo
Sin embargo, en las visitas posteriores, todo el recurso .css se almacenaría en caché, por lo que FCP y LCP se verían impactados positivamente.
Puntos clave
Esta solución parece ser más adecuada cuando:
.css común que se puede almacenar en caché al visitar otras páginas;<style> estilos inyectados de etiqueta Durante la SSR , los estilos se agregarán como <style> etiquetas en el <head> de la página. Tenga en cuenta que estos generalmente no incluyen todos los estilos necesarios para la página, porque la mayoría de las bibliotecas realizan una extracción crítica de CSS, por lo que estos styles generalmente deberían ser más pequeños que todo el archivo estático .css discutido anteriormente.
? Caché vacío
Debido a que estamos enviando menos bytes CSS, y se ingresan dentro del archivo .html , esto daría como resultado FCP y LCP más rápidos:
.css , por lo que el navegador no está bloqueado;.js al final del documento, <head> no hará ninguna solicitud, por lo que la representación ocurrirá muy rápido;.css :.js junto con los componentes, durante la hidratación (esto incluye todos los CSS críticos ya enviados dentro de la etiqueta <style> ); ? Caché completo
Cuando el caché del usuario está lleno, los archivos .js adicionales no requerirán recuperar, ya que ya están almacenados en caché.
Sin embargo, si la página es ssred , el CSS crítico en línea en la etiqueta <style> del documento se descargará nuevamente, a menos que tratemos con HTML estático que también pueda almacenarse en caché, o tratemos con HTML en caché de nuestra infraestructura.
Pero, por defecto, enviaremos bytes adicionales en cada solicitud de HTTP, independientemente de si está en caché o no.
Puntos clave
Esta solución parece ser más adecuada cuando:
La mayoría de las soluciones dicen que eliminan el código/estilos no utilizados . Esto es solo medio verdad .
El código no utilizado es de hecho más difícil de acumular, especialmente si lo compara con archivos .css simples como solíamos escribir hace una década . Pero en comparación con los módulos CSS, las diferencias no son tan grandes. Cualquier solución que ofrezca la opción de definir selectores arbitrarios o estilos anidados los agrupará, independientemente de si se usan o no dentro de nuestro componente. Hemos logrado enviar estilos de SSR no utilizados con todas las soluciones probadas.
La eliminación de código verdadero y completo no utilizada es difícil de implementar, ya que la sintaxis CSS no está verificada de tipo, ni analizado estáticamente. Además, la naturaleza dinámica de los componentes lo hace prácticamente imposible en ciertos escenarios, especialmente cuando el marcado se representa dinámicamente:
& span : elementos descendientes;&:nth-child() : ciertos pseudo selectores;& .bg-${color} : selectores dinámicos;.parent & : SELECTORES PADRES;Básicamente, lo que obtenemos es eliminar el código cuando eliminamos el componente, o ya no lo importamos. Ese es un comportamiento implícito, porque los estilos son una dependencia directa del componente. Cuando el componente se ha ido, también lo son sus estilos.
Hay 2 métodos para inyectar CSS en el DOM y actualizarlo desde JavaScript:
<style> ) <s) <S) Este enfoque implica agregar una o más etiquetas <style> s) <s) en el DOM (ya sea en <head> o en algún lugar del <body> ), usando .AppendChild () para agregar los Nodos <style> S) <S), además de .TextContent, .innerhtml para actualizar la etiqueta <style> .
<style> y actualizar todo su contenido, podría ser lento para actualizar todo el DOM cuando realmente cambiamos solo un pequeño conjunto de reglas (s) CSS;DEVELOPMENT , porque proporciona una mejor experiencia de depuración;PRODUCTION también; CSSStyleSheet Utilizado por primera vez por JSS , este método utiliza CSSStyleSheet.insertRule() para inyectar reglas CSS directamente en el CSSOM .
<style> vacía;<style> ;$0 en Chrome Devtools (o obtenga una referencia de cualquier otra manera, usando la API DOM);.sheet.cssRules en la etiqueta <style> para ver la matriz de reglas CSS que contiene;PRODUCTION ;DEVELOPMENT ; Si el mismo componente es importado por 2 rutas diferentes, se enviará dos veces al cliente. Esto seguramente es una limitación del sistema Bundler/Build, en nuestro caso Next.js, y no está relacionado con la solución CSS-in-JS .
En Next.js, la división de código funciona en el nivel de ruta, agrupando todos los componentes necesarios para una ruta específica, pero de acuerdo con su blog oficial y web.dev si un componente se usa en más del 50% de las páginas, debe incluirse en el paquete commons . Sin embargo, en nuestro ejemplo, tenemos 2 páginas, cada una de ellas importando el componente Button , y está incluido en cada paquete de páginas, no en el paquete commons . Dado que el código requerido para el estilo se incluye con el componente, esta limitación también afectará los estilos, por lo que vale la pena tener esto en cuenta.
Este es un enfoque bien establecido, maduro y sólido. Sin duda, es una gran mejora sobre BEM, SMACCS, OOCSS o cualquier otra metodología CSS escalable para estructurar y organizar nuestro CSS, especialmente en aplicaciones basadas en componentes.
Lanzado en 2015 | Volver a la descripción general
✅ Completar el código con el contexto
✅ Marco agnóstico
No hay estilos/componentes de ubicación conjunta
Sin soporte de mecanografiado
No CSS atómico
Sin apoyo de temas
Método (s) de definición de estilos
Estilos de anidación
Estilos Aplicar métodos (s)
classNamestyledcss CSSSalida de estilos
.css<style> inyección de etiquetasEsta es la línea de base que consideraremos al comparar todas las siguientes soluciones CSS-in-JS . Consulte la motivación para comprender mejor las limitaciones de este enfoque que estamos tratando de llenar.
| Transferido / gzipped | Sin comprimir | |
|---|---|---|
| Tamaño de la página del índice | 76.7 kb | 233 kb |
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
Solución muy simple, no tiene un sitio web dedicado para la documentación, todo está en GitHub. No es popular, pero es la solución incorporada en Next.js.
Versión: 4.0 | Mantenido por Verccel | Lanzado en 2017 | Ver documentos | ... Volver a la descripción general
✅ Estilos/componentes Co-ubicación
? Finalización del código consciente del contexto : para obtener la resaltura de sintaxis y la finalización del código, se requiere una extensión del editor
? Soporte de TypeScript : @types se puede instalar adicionalmente, pero la API es demasiado mínima para requerir TS
No CSS atómico
Sin apoyo de temas
No marco agnóstico
Método (s) de definición de estilos
Estilos de anidación
Estilos Aplicar métodos (s)
classNamestyledcss CSSSalida de estilos
.css<style> inyección de etiquetas elements HTML, y genera nombres de clases únicos para ellos (sin embargo, no estoy seguro de si es una buena práctica) En general, sentimos como escritura CSS simple, con el beneficio adicional de poder definir los estilos junto con el componente, por lo que no necesitamos un archivo .css adicional . De hecho, esta es la filosofía de la biblioteca: admitir la sintaxis CSS dentro del archivo de componentes. Podemos usar cualquier constante JS/TS de funciones con interpolación de cadenas. Trabajar con estilos dinámicos es bastante fácil porque al final es JavaScript simple. Obtenemos todos estos beneficios a un precio muy bajo, con un paquete bastante pequeño .
Las desventajas son la experiencia general de escribir CSS simple. Sin anidar soporte, las clases/elementos de pseudo y las consultas de los medios se vuelven bastante engorrosos para manejar.
| Transferido / gzipped | Sin comprimir | |
|---|---|---|
| Tamaño de la página del índice | 79.5 kb | 245 kb |
| vs. módulos CSS | +2.8 kb | +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
Por supuesto, una de las soluciones más populares y maduras, con buena documentación. Utiliza plantillas etiquetadas para definir estilos de forma predeterminada, pero también puede usar objetos. También popularizó el enfoque de componentes styled , que crea un nuevo componente junto con los estilos definidos.
Versión: 5.3 | Mantenido por Max Stoiber y otros | Lanzado en 2016 | Ver documentos | ... Volver a la descripción general
✅ Estilos/componentes Co-ubicación
✅ Soporte de TypeScript : @types debe instalarse adicionalmente, a través de definitivamente Tytedped
✅ Soporte de temas incorporado
✅ Marco agnóstico
? Finalización del código consciente del contexto : requiere una extensión/complemento del editor
No CSS atómico
Método (s) de definición de estilos
Estilos de anidación
Estilos Aplicar métodos (s)
classNamestyledcssSalida de estilos
.css<style> inyección de etiquetas Props , etc.) Los componentes de estilo ofrecen un enfoque novedoso para el estilo componentes utilizando el método styled que crea un nuevo componente que incluye los estilos definidos. No tenemos ganas de escribir CSS, por lo que provenir de los módulos CSS tendremos que aprender una forma nueva y más programática, para definir estilos. Debido a que permite tanto la sintaxis string como object , es una solución bastante flexible tanto para migrar nuestros estilos existentes como para comenzar un proyecto desde cero. Además, los mantenedores hicieron un trabajo bastante bueno manteniéndose al día con la mayoría de las innovaciones en este campo.
Sin embargo, antes de adoptarlo, debemos ser conscientes de que viene con un cierto costo para nuestro tamaño de paquete.
| Transferido / gzipped | Sin comprimir | |
|---|---|---|
| Tamaño de la página del índice | 90.1 kb | 272 kb |
| vs. módulos CSS | +13.4 kb | +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
Probablemente la solución más completa, completa y sofisticada. Documentación detallada, totalmente construida con mecanografiado, se ve muy maduro, rica en características y bien mantenida.
Versión: 11.4 | Mantenido por Mitchell Hamilton y otros | Lanzado en 2017 | Ver documentos | ... Volver a la descripción general
✅ Estilos/componentes Co-ubicación
✅ Soporte mecanografiado
✅ Soporte de temas incorporado
✅ Completar el código de contexto : para usar el enfoque de componentes styled , se requiere un complemento de editor adicional
✅ Marco agnóstico
No CSS atómico
Método (s) de definición de estilos
Estilos de anidación
Estilos Aplicar métodos (s)
className (usando @emoción/css)styledcssSalida de estilos
.css<style> inyección de etiquetas css ofrece una gran ergonomía durante el desarrollo, sin embargo, parece ser un enfoque más nuevo, basado en React 17 New jsx Transform, y configurarla no es trivial, difiere en su configuración e implica algo de calderla (que debería cambiar pronto y ser más fácil) styled agregará 3 kB a nuestro paquete, porque se importa de un paquete separadocss Prop & styled ) La emoción general parece ser un enfoque muy sólido y flexible. El nuevo enfoque css Prop ofrece una gran ergonomía para los desarrolladores. Trabajar con estilos dinámicos y mecanografiados es bastante fácil e intuitivo. Apoyando tanto strings como objects al definir estilos, se puede usar fácilmente tanto al migrar desde CSS liso o comenzar desde cero. La sobrecarga del paquete no es insignificante, pero definitivamente mucho más pequeño que otras soluciones, especialmente si considera el rico conjunto de características que ofrece.
Parece que no tiene un enfoque dedicado en el rendimiento, pero más en la experiencia del desarrollador. Parece una solución perfecta "bien redondeada".
| Transferido / gzipped | Sin comprimir | |
|---|---|---|
| Tamaño de la página del índice | 83.2 kb | 253 kb |
| vs. módulos CSS | +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
Biblioteca mínima, centrada solo en la verificación de tipo. 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: