Este documento contém uma análise aprofundada de todas as soluções atuais do CSS-In-JS , que suportam a renderização e a digitalização do servidor.
A referência de linha de base que usaremos para comparação é uma abordagem de módulos CSS .
Estamos usando o Next.js como uma estrutura SSR para a construção de recursos.
O último aspecto importante é a segurança do tipo com o suporte completo do texto datilografado .
? Última atualização: agosto de 2021
? Para obter uma visão geral mais curta, você pode verificar o artigo sobre truques CSS :
https://css-tricks.com/a-thorough-analysis-of-css-in-js/
? Se você preferir um vídeo, pode verificar minha palestra de ngpartycz :
https://www.youtube.com/watch?v=c7uwghrax9a
Confira nossos objetivos e isenção de responsabilidade antes de tirar conclusões.
Os módulos CSS Language e CSS têm algumas limitações, especialmente se quisermos ter código seguro. Algumas dessas limitações têm soluções alterativas, outras estão sendo irritantes ou menos do que o ideal :
Os estilos não podem ser co-localizados com componentes
Isso pode ser frustrante ao criar muitos pequenos componentes, mas não é um quebra de negócio. No entanto, a experiência de mover-se e partir para o arquivo component.js e o arquivo component.css , pesquisar um determinado nome de classe e não ser capaz de "ir para a definição de estilo" facilmente é uma importante desvantagem da produtividade.
Pseudos de estilo e consultas de mídia requer duplicação seletora
Outro fato frustrante é a necessidade de duplicar nossas classes CSS ao definir aulas e elementos de pseudo -elementos , ou consultas de mídia . Podemos superar essas limitações usando um pré -processador de CSS como SASS, Menos ou Stylus , que suporta o seletor & dos pais, permitindo o 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 {}
} O uso de estilos está desconectado de sua definição
Não temos o IntelliSense com os módulos CSS, do que as classes CSS são definidas no arquivo component.css , tornando a cópia uma ferramenta necessária, diminuindo o DX. Também torna a refatoração muito pesada , devido à falta de segurança.
Usar tokens de design segura
Quaisquer tokens de design definidos em JS/TS (para se beneficiar da segurança do tipo) não podem ser usados diretamente no CSS.
Existem pelo menos 2 soluções alternativas para esta questão, nenhum deles sendo elegante:
.module.css ..css .Existem objetivos específicos que estamos procurando com esta análise:
Ficando ainda mais específico, queríamos experimentar o uso de várias soluções CSS-In-JS sobre:
props de componentes (também conhecidos como variantes de componentes) ou da entrada do usuárioEsta análise pretende ser objetiva e poucopinionada :
? O que você não encontrará aqui?
? O que você encontrará aqui?
As bibliotecas não são apresentadas em nenhuma ordem específica. Se você estiver interessado em uma breve história do CSS-In-JS , deve verificar o passado, o presente e o futuro da conversa perspicaz do CSS-In-JS de Max Stoiber.
| 1. Co -localização | 2. DX | 3. tag` ` | 4. { } | 5. TS | 6. & CTX | 7. Ninho | 8. Tema | .css | 10. <style> | 11. Atomic | 12. className | 13. <Styled /> | 14. css Prop | 15. AGNOSTIC | 16. Tamanho da página Delta | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Módulos CSS | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | - | |||||||||
| JSX denominado | ✅ | ? | ✅ | ? | ✅ | ✅ | ✅ | +2.8 kB / +12.0 kB | ||||||||
| Componentes estilizados | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +13.4 kB / +39.0 kB | ||||
| Emoção | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +6.5 kB / +20.0 kB | ||
| Typestyle | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | ✅ | +2.1 kB / +8.0 kB | |||||
| Fela | ✅ | ? | ? | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +11.9 kB / +43.0 kB | |||
| Pontos | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +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 | ||||
| baunilha-extrato | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | +0.0 kB / -2.0 kB |
A capacidade de definir estilos dentro do mesmo arquivo que o componente. Observe que também podemos extrair os estilos para um arquivo separado e importá -los, caso prefira.
⬆️ para a visão geral
Refere -se à experiência do desenvolvedor , que inclui 2 aspectos principais:
⬆️ para a visão geral
tag` ` (modelos marcados)Suporte para definir estilos como strings , usando modelos de etiquetas ES:
kebab-case para nomes de propriedades, assim como a sintaxe CSS simples;string simples;⬆️ para a visão geral
{ } (Estilos de objeto)Suporte para definir estilos como objetos , usando objetos JavaScript simples:
camelCase para nomes de propriedades, como faríamos no React Native;⬆️ para a visão geral
Suporte TypeScript, embutido ou via pacote @types , que deve incluir:
Props genéricos, quando aplicável (obtenha acesso à segurança do tipo aos tipos de adereços componentes ao definir estilos dinâmicos);⬆️ para a visão geral
& CTX (estilos contextuais)Suporte para estilos contextuais, permitindo -nos definir facilmente as classes e elementos e consultas de mídia sem a necessidade de repetir o seletor, conforme necessário no CSS simples:
& PAI;⬆️ para a visão geral
Suporte a seletores aninhados arbitrários :
⬆️ para a visão geral
Suporte interno para temas ou gerenciamento de tokens para um sistema de design.
Não testamos esse recurso , por isso estamos apenas fazendo anotações quais bibliotecas expressam seu apoio em seus documentos.
⬆️ para a visão geral
.css (extração estática de CSS) Os estilos definidos são extraídos como arquivos .css estáticos:
⬆️ para a visão geral
<style> tag Os estilos definidos são injetados dentro das tags <style> no <head> do documento:
⬆️ para a visão geral
A capacidade de gerar classes atômicas de CSS , aumentando a reutilização de estilo e reduzindo a duplicação:
⬆️ para a visão geral
className A API da biblioteca retorna uma string que devemos adicionar ao nosso componente ou elemento;
⬆️ para a visão geral
<Styled /> A API cria um componente de invólucro (ou Styled ) que inclui o (s) className gerado:
StyledButton ou StyledList em vez de constantes como button_styles ou list_styles , portanto, sobre nomear é praticamente a mesma coisa;⬆️ para a visão geral
css Prop Permite a passagem de estilos usando um suporte css especial, semelhante a definir estilos embutidos, mas a biblioteca gera um nome de classe CSS exclusivo nos bastidores:
⬆️ para a visão geral
Permite o uso sem ou com qualquer estrutura. Algumas bibliotecas são construídas especificamente apenas para reação.
NOTA : Algumas bibliotecas como pontos ou documentos de emoção reagem apenas o uso, embora tenham um núcleo que seja a estrutura agnóstica.
⬆️ para a visão geral
A diferença de tamanho total da página em KB (transferido Gzipped & Minified / não compactado e minificado) em comparação com os módulos CSS , para toda a construção da produção de página de índice usando o Next.js:
NOTA: Todas as compilações foram feitas com o próximo.js 11.1.0 e os valores são retirados da guia Chrome Devtools, transferidos por rede de rede versus tamanho de recurso.
⬆️ para a visão geral
As seguintes observações se aplicam a todas as soluções (com pequenas exceções pontiagudas).
Os componentes usados apenas em uma rota específica serão incluídos apenas para essa rota. Isso é algo que o Next.js executa fora da caixa.
Todas as soluções oferecem uma maneira de definir estilos globais, alguns com uma API dedicada.
Todas as soluções oferecem suporte para renderização do lado do servidor e são fáceis de integrar com o Next.JS.
Todas as soluções adicionam automaticamente prefixos específicos do fornecedor.
Todas as soluções geram nomes de classe exclusivos, como os módulos CSS. O algoritmo usado para gerar esses nomes varia muito entre as bibliotecas:
.heading bibliotecas usam Card algoritmo de hash , exigindo mais computação, mas resultando em nomes idempotentes (por .Card_heading_h7Ys5 :..heading-0-2-1 , .input-0-2-2 ) ou as letras do alfabeto ( a, b, c, ... aa, ab, ac , etc.), tornando essa abordagem mais performante, mas resultando em nomes de classe não idepóticos (não conseguem descobrir se isso possui nenhum dolares em potencial ou não); Nenhuma das soluções gera estilos embutidos, que é uma abordagem mais antiga, usada pelo Radium & Glamour. A abordagem é menos executada que as classes CSS e não é recomendada como método primário para definir estilos. Também implica o uso de manipuladores de eventos JS para acionar as classes de pseudo, pois os estilos embutidos não os suportam. Aparentemente, todas as soluções modernas hoje em dia se afastaram dessa abordagem.
Todas as soluções suportam a maioria das propriedades do CSS que você precisaria: Pseudo -Classes & Elements , Media Consultes e Keyframes são as que testamos.
A maioria das soluções se comercializa como capaz de "extrair CSS críticos" durante a SSR. Observe que isso não se refere à extração crítica do CSS acima da dobra, como pensamos inicialmente.
O que eles realmente fazem:
Com CSS 100% estático, na verdade não haveria benefício. Com páginas dinâmicas que renderizam muito poucos elementos no servidor, e a maioria dos componentes é renderizada dinamicamente no cliente, o benefício aumenta.
Exceção : bibliotecas que usam extração estática CSS.
Compreender como esses recursos afetam os principais vitais da Web e as métricas de desempenho em geral é um fator extremamente importante a ser considerado, e a maneira como os estilos são entregues ao cliente provavelmente tem o maior impacto, então vamos analisar isso em detalhes.
Além disso, existem 2 cenários diferentes que precisamos considerar:
.js , .css , mídia, etc); .css Extração de arquivos As soluções que geram arquivos estáticos .css , que você normalmente incluiria como tag (s) <link> na (s) <head> da sua página, estão basicamente renderizando recursos de bloqueio. Isso afeta muito o FCP , LCP e qualquer outra métrica que se segue.
? Cache vazio
Se o usuário tiver um cache vazio, o seguinte precisará acontecer, impactando negativamente o FCP e o LCP :
<body> , mesmo que todo o HTML já esteja carregado, e pode até ser analisado ansiosamente, e alguns recursos já obtidos com antecedência; É verdade que você pode buscar em paralelo outros recursos <head> (arquivos .css ou .js adicionais), mas essa geralmente é uma prática ruim;
? Cache completo
No entanto, nas visitas subsequentes, todo o recurso .css seria armazenado em cache, para que o FCP e o LCP fossem impactados positivamente.
Pontos -chave
Esta solução parece ser mais adequada quando:
.css comum que pode ser armazenado em cache ao visitar outras páginas;<style> estilos injetados da tag Durante a SSR , os estilos serão adicionados como tags <style> na (s) <head> da página. Lembre -se de que estes geralmente não incluem todos os estilos necessários para a página, porque a maioria das bibliotecas executa a extração crítica do CSS; portanto, esses styles geralmente devem ser menores do que o arquivo estático .css inteiro discutido anteriormente.
? Cache vazio
Como estamos enviando menos bytes de CSS e eles estão inlinados dentro do arquivo .html , isso resultaria em FCP e LCP mais rápido:
.css , para que o navegador não esteja bloqueado;.js para o final do documento, <head> não fará nenhuma solicitação, portanto, a renderização ocorrerá super rapidamente;.css :.js junto com os componentes, durante a hidratação (isso inclui todos os CSs críticos já enviados dentro da tag <style> tag + outros); ? Cache completo
Quando o cache do usuário está cheio, os arquivos .js adicionais não exigirão buscar, pois já estão em cache.
No entanto, se a página for ssred , o CSS crítico inlinado renderizado na tag <style> do documento será baixado novamente, a menos que lidemos com HTML estático que também possa ser armazenado em cache ou lidamos com o cache HTML em nossa infraestrutura.
Mas, por padrão, enviaremos bytes extras em todas as solicitações HTTP de página, independentemente de ser em cache ou não.
Pontos -chave
Esta solução parece ser mais adequada quando:
A maioria das soluções diz que remove o código/estilos não utilizados . Isso é apenas meio verdadeiro .
O código não utilizado é realmente mais difícil de acumular, especialmente se você o comparar com os arquivos .css simples, como costumávamos escrever há uma década . Mas quando comparados aos módulos CSS, as diferenças não são tão grandes. Qualquer solução que ofereça a opção de definir seletores arbitrários ou estilos aninhados os agrupará, independentemente de serem usados ou não dentro do nosso componente. Conseguimos enviar estilos SSR não utilizados com todas as soluções testadas.
A remoção de código verdadeira e completa não utilizada é difícil de implementar, pois a sintaxe do CSS não é verificada pelo tipo nem analisável estaticamente. Além disso, a natureza dinâmica dos componentes torna praticamente impossível em certos cenários, especialmente quando a marcação é dinamicamente renderizada:
& span : elementos descendentes;&:nth-child() : certos seletores de pseudo;& .bg-${color} : seletores dinâmicos;.parent & : Seletores de pais;Basicamente, o que obtemos é a remoção de código quando excluímos o componente, ou não o importamos mais. Esse é um comportamento implícito, porque os estilos são uma dependência direta do componente. Quando o componente se foi, seus estilos também.
Existem 2 métodos para injetar CSS no DOM & atualizá -lo do JavaScript:
<style> Essa abordagem implica adicionar uma ou mais tag (s) <style> no (s) Dom (no <head> ou em algum lugar do <body> ), usando .appendchild () para adicionar o <style> nó (s), além de .TextContent, .innerHtml Atualizar as tags <style> ).
<style> e atualizando todo o seu conteúdo, pode demorar a atualizar todo o DOM quando realmente alteramos apenas um pequeno conjunto de regras CSS;DEVELOPMENT , porque oferece uma melhor experiência de depuração;PRODUCTION ; CSSStyleSheet Utilizado pela primeira vez pelo JSS , esse método usa CSSStyleSheet.insertRule() para injetar regras CSS diretamente no CSSOM .
<style> vazia;<style> ;$0 em Chrome Devtools (ou obtenha uma referência a ele de qualquer outra maneira, usando a API DOM);.sheet.cssRules na tag <style> para ver a matriz de regras CSS que ela contém;PRODUCTION ;DEVELOPMENT ; Se o mesmo componente for importado por 2 rotas diferentes, ele será enviado duas vezes para o cliente. Isso certamente é uma limitação do sistema de criação/construção, em nosso caso, a seguir, e não está relacionada à solução CSS-in-JS .
No Next.js, a divisão de código funciona no nível da rota, agrupando todos os componentes necessários para uma rota específica, mas de acordo com o blog oficial e o web.dev se um componente for usado em mais de 50% das páginas, ele deve ser incluído no pacote commons . No entanto, em nosso exemplo, temos 2 páginas, cada uma delas importando o componente Button , e está incluído em cada pacote de páginas, não no pacote do commons . Como o código necessário para o estilo é agrupado com o componente, essa limitação também afetará os estilos, por isso vale a pena ter isso em mente.
Esta é uma abordagem bem estabelecida, madura e sólida. Sem dúvida, é uma grande melhoria em relação ao BEM, SMACCS, OOCSS ou qualquer outra metodologia CSS escalável para estruturar e organizar nossos CSs, especialmente em aplicações baseadas em componentes.
Lançado em 2015 | De volta à visão geral
✅ Conclusão do código com reconhecimento de contexto
✅ Estrutura agnóstica
Sem estilos/co-localização de componentes
Sem suporte datilografado
Sem CSS atômico
Não há apoio de temas
Métodos de definição de estilos
Estilos nidificações
Estilos aplicam métodos (s) (s)
classNamestyledcss PropSaída de estilos
.css<style> tagsEsta é a linha de base que consideraremos ao comparar todas as seguintes soluções CSS-in-JS . Confira a motivação para entender melhor as limitações dessa abordagem que estamos tentando preencher.
| Transferido / gzipped | Não compactado | |
|---|---|---|
| Tamanho da página do í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
Solução muito simples, não possui um site dedicado para documentação, tudo está no GitHub. Não é popular, mas é a solução interna no Next.JS.
Versão: 4.0 | Mantido por Vercel | Lançado em 2017 | Ver docs | ... de volta à visão geral
✅ Estilos/co-localização de componentes
? Conclusão do código com reconhecimento de contexto : Para obter sintaxe Destaque e conclusão de código, é necessária uma extensão do editor
? Suporte ao TypeScript : @types pode ser instalado adicional, mas a API é mínima demais para exigir TS
Sem CSS atômico
Não há apoio de temas
Não estrutura agnóstica
Métodos de definição de estilos
Estilos nidificações
Estilos aplicam métodos (s) (s)
classNamestyledcss PropSaída de estilos
.css<style> injeção de tags elements HTML e gera nomes de classe exclusivos para eles (não tenho certeza se é uma boa prática, no entanto) No geral, sentimos vontade de escrever CSS simples, com o benefício adicional de poder definir os estilos junto com o componente, para que não precisemos de um arquivo .css adicional . De fato, esta é a filosofia da biblioteca: suportando a sintaxe do CSS dentro do arquivo componente. Podemos usar quaisquer constantes JS/TS de funções com interpolação de string. Trabalhar com estilos dinâmicos é muito fácil porque é JavaScript simples no final. Temos todos esses benefícios a um preço muito baixo, com uma sobrecarga de pacote muito pequena .
As desvantagens são a experiência geral de escrever CSS simples. Sem o ninho, apoie as aulas/elementos de pseudo e consultas de mídia ficando bastante pesadas para gerenciar.
| Transferido / gzipped | Não compactado | |
|---|---|---|
| Tamanho da página do í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
Com certeza uma das soluções mais populares e maduras, com boa documentação. Ele usa modelos marcados para definir estilos por padrão, mas também podem usar objetos. Ele também popularizou a abordagem de componentes styled , que cria um novo componente junto com os estilos definidos.
Versão: 5.3 | Mantido por Max Stoiber e outros | Lançado em 2016 | Ver docs | ... de volta à visão geral
✅ Estilos/co-localização de componentes
✅ Suporte ao TypeScript : @types deve ser instalado adicional, via definitivamente
✅ Suporte de tema embutido
✅ Estrutura agnóstica
? Conclusão do código com reconhecimento de contexto : requer uma extensão/plug-in do editor
Sem CSS atômico
Métodos de definição de estilos
Estilos nidificações
Estilos aplicam métodos (s) (s)
classNamestyledcss PropSaída de estilos
.css<style> injeção de tags Props , etc.) Os componentes estilizados oferecem uma nova abordagem para modelar componentes usando o método styled , que cria um novo componente, incluindo os estilos definidos. Não sentimos vontade de escrever CSS; portanto, vindo dos módulos CSS, teremos que aprender uma maneira nova e mais programática, para definir estilos. Como permite a sintaxe string e object , é uma solução bastante flexível para migrar nossos estilos existentes e para iniciar um projeto do zero. Além disso, os mantenedores fizeram um bom trabalho acompanhando a maioria das inovações nesse campo.
No entanto, antes de adotá -lo, devemos estar cientes de que ele tem um certo custo para o tamanho do nosso pacote.
| Transferido / gzipped | Não compactado | |
|---|---|---|
| Tamanho da página do í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
Provavelmente a solução mais abrangente, completa e sofisticada. A documentação detalhada, totalmente construída com o TypeScript, parece muito maduro, rico em recursos e bem conservado.
Versão: 11.4 | Mantido por Mitchell Hamilton e outros | Lançado em 2017 | Ver docs | ... de volta à visão geral
✅ Estilos/co-localização de componentes
✅ Suporte ao TypeScript
✅ Suporte de tema embutido
✅ Conclusão do código com reconhecimento de contexto : Para usar a abordagem de componentes styled , é necessário um plug-in adicional do editor
✅ Estrutura agnóstica
Sem CSS atômico
Métodos de definição de estilos
Estilos nidificações
Estilos aplicam métodos (s) (s)
className (usando @emoção/css)styledcss PropSaída de estilos
.css<style> injeção de tags css oferece grande ergonomia durante o desenvolvimento, no entanto, parece ser uma abordagem mais recente, baseada no React 17 New jsx Transform, e configurá -la não é trivial, difere na sua configuração e implica alguma caldeira (o que deve mudar em breve e se tornar mais fácil) styled adicionará 3 kB ao nosso pacote, porque é importado de um pacote separadocss Prop & componentes styled ) A emoção geral parece ser uma abordagem muito sólida e flexível. A nova abordagem de suporte css oferece grande ergonomia para os desenvolvedores. Trabalhar com estilos dinâmicos e o TypeScript é bastante fácil e intuitivo. Suportando strings e objects ao definir estilos, ele pode ser facilmente usado tanto ao migrar de CSS simples quanto a partir do zero. A sobrecarga do pacote não é insignificante, mas definitivamente muito menor que outras soluções, especialmente se você considerar o rico conjunto de recursos que ele oferece.
Parece que não tem um foco dedicado no desempenho, mas mais na experiência do desenvolvedor. Parece uma solução perfeita "bem-arredondada".
| Transferido / gzipped | Não compactado | |
|---|---|---|
| Tamanho da página do í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
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: