本文檔包含對所有當前CSS-IN-JS解決方案的深入分析,該解決方案支持服務器端渲染和打字稿。
我們將用於比較的基線參考是CSS模塊方法。
我們將Next.js用作建築資源的SSR框架。
最後的重要方面是帶有完整標題支持的類型安全。
?上次更新: 2021年8月
?要獲得較短的概述,您可以在CSS技巧上查看文章:
https://css-tricks.com/a-thorough-analysis-of-css-in-js/
?如果您喜歡視頻,則可以從Ngpartycz結帳我的演講:
https://www.youtube.com/watch?v=c7uwghrax9a
請在得出結論之前查看我們的目標和免責聲明。
CSS語言和CSS模塊有一些局限性,尤其是在我們想具有類型安全代碼的情況下。這些限制中的一些具有改變的解決方案,其他限制只是令人討厭或不理想:
樣式不能與組件共同置於
在創作許多小型組件時,這可能會令人沮喪,但這並不是一個破壞交易的人。但是,在component.js文件和component.css文件,搜索給定的類名稱以及無法輕鬆“轉到樣式定義”之間的back and Forth的經驗是一個重要的生產力缺陷。
樣式偽和媒體查詢需要選擇器重複
另一個令人沮喪的事實是,在定義偽類和元素或媒體查詢時需要復制我們的CSS課程。我們可以使用支持&父母選擇器的CSS預處理器(例如Sass,Limes或Stylus)來克服這些限制,從而實現上下文樣式。
. button {}
/* duplicated selector declaration for pseudo classes/elements */
. button : hover {}
. button :: after {}
@media ( min-width : 640 px ) {
/* duplicated selector declaration inside media queries */
. button {}
}樣式的使用與他們的定義斷開
我們沒有CSS模塊的IntelliSense,其中CSS類在component.css文件中定義了哪些類別,使Copy-Paste成為必需的工具,從而降低DX。由於缺乏安全性,這也使重構非常繁瑣。
在CSS中使用類型安全的設計令牌是非平凡的
CSS中不能直接使用JS/TS中定義的任何設計令牌。
這個問題至少有2個解決方法,它們都不優雅:
.module.css中使用它們時,我們仍然不會獲得任何IntelliSense或類型安全。.css文件。我們正在尋找此分析的具體目標:
更具體地說,我們想體驗有關以下情況的各種CSS-IN-JS解決方案的使用:
props (又稱組件變體)或用戶輸入的動態樣式該分析旨在是客觀且未經公開的:
?您在這裡找不到什麼?
?您會在這裡找到什麼?
圖書館均不以任何特定順序呈現。如果您對CSS-In-JS的簡短歷史感興趣,則應查看Max Stoiber的CSS-In-Js Indight Full Talk的過去,現在和未來。
| 1。共同定位 | 2。 DX | tag` ` | 4。 { } | 5。 TS | 6。 & CTX | 7。築巢 | 8。主題 | .css | 10。 <style> | 11。原子 | className | 13。 <Styled /> | css道具 | 15。不可知論 | 16。頁面大小三角洲 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CSS模塊 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | - | |||||||||
| 風格的JSX | ✅ | ? | ✅ | ? | ✅ | ✅ | ✅ | +2.8 kB / +12.0 kB | ||||||||
| 樣式的組件 | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +13.4 kB / +39.0 kB | ||||
| 情感 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +6.5 kB / +20.0 kB | ||
| 打字機 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | ✅ | +2.1 kB / +8.0 kB | |||||
| 費拉 | ✅ | ? | ? | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +11.9 kB / +43.0 kB | |||
| 針跡 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +5.3 kB / +17.0 kB | |||
| JSS | ✅ | ✅ | ? | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +18.2 kB / +60.0 kB | |||
| 戈貝 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | +1.1 kB / +4.0 kB | ||
| 編譯 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | +3.5 kB / +9.0 kB | |||
| Linaria | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +2.7 kB / +6.0 kB | ||||
| 香草提取 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ? | ✅ | ✅ | +0.0 kB / -2.0 kB |
與組件在同一文件中定義樣式的能力。請注意,如果我們喜歡它,我們還可以將樣式提取到單獨的文件中並導入它們。
⬆️概述
指開發人員體驗,其中包括兩個主要方面:
⬆️概述
tag` ` (標記模板)支持將樣式定義為字符串,使用ES標記模板:
kebab-case用作屬性名稱;string ;⬆️概述
{ } (對像樣式)支持將樣式定義為對象,使用普通的JavaScript對象:
camelCase用於屬性名稱,就像我們在React Native中所做的一樣;⬆️概述
打字稿支持,無論是內置的還是@types軟件包,其中應包括:
Props Generics,如果適用(定義動態樣式時,可以獲取對組件類型類型的類型訪問);⬆️概述
& CTX(上下文樣式)支持上下文樣式,使我們可以輕鬆地定義偽類,元素和媒體查詢,而無需按照普通CSS的要求重複選擇器:
& Parent Selector;⬆️概述
支持任意嵌套的選擇器:
⬆️概述
內置支持設計系統的主題或管理令牌。
我們尚未測試此功能,因此我們只記下圖書館在文檔中表達支持的筆記。
⬆️概述
.css (靜態CSS提取)定義樣式被提取為靜態.css文件:
⬆️概述
<style>標籤定義的樣式被注入文檔的<head>中的<style>標籤:
⬆️概述
產生原子CSS類的能力,從而提高樣式可重複性並減少重複:
⬆️概述
className庫API返回一個我們必須添加到組件或元素的string ;
⬆️概述
<Styled /> API創建一個包裝器(或Styled )組件,其中包括生成的className :
StyledButton或StyledList之類的組件,而不是button_styles或list_styles等常數,因此,關於命名它幾乎是相同的事情;⬆️概述
css道具允許使用特殊的css道具傳遞樣式,類似我們將如何定義內聯樣式,但是庫在幕後生成了唯一的CSS類名稱:
⬆️概述
允許無需使用或使用任何框架。一些庫是專門為反應而構建的。
注意:一些圖書館(例如針跡或情感文檔)僅反應用法,儘管它們具有框架不可知的核心。
⬆️概述
與CSS模塊相比,KB的總頁面尺寸差異(轉移的GZZEND和MINIFIED / UNIFIED / UNIFECTRESS和MINIFIED),使用Next.js:整個索引頁面生產:
注意:所有構建均使用Next.JS 11.1.0完成,並且值取自Chrome DevTools網絡選項卡,通過網絡與資源大小傳輸。
⬆️概述
以下觀察結果適用於所有解決方案(除次要例外)。
僅在特定路線中使用的組件只會捆綁在該路線上。這是Next.js執行開箱即用的東西。
所有解決方案都提供了一種定義全球樣式的方法,其中一些具有專用API。
所有解決方案均提供服務器端渲染支持,並且易於與Next.js集成。
所有解決方案都會自動添加特定於供應商的前綴。
所有解決方案都會產生獨特的類名稱,例如CSS模塊。用於生成這些名稱的算法在庫之間有很大不同:
Card組件的.heading樣式始終具有.Card_heading_h7Ys5 hash);.heading-0-2-1 , .input-0-2-2 )或字母字母( a, b, c, ... aa, ab, ac等)上增加一個數字(.head-0-2-1,.input-0-2-2),使這種方法具有更大的性能,但導致非勢力的類名稱(無法弄清楚這是否具有任何潛在的草圖或沒有潛在的草圖); radium&Glamor使用的任何解決方案均未產生內聯樣式,這是一種較舊的方法。該方法的性能低於CSS類,也不建議作為定義樣式的主要方法。這也意味著使用JS事件處理程序來觸發偽類,因為內聯樣式不支持它們。顯然,如今的所有現代解決方案都擺脫了這種方法。
所有解決方案都支持您需要的大多數CSS屬性:偽類和元素,媒體查詢和關鍵幀是我們測試的。
大多數解決方案本身都可以在SSR期間“提取關鍵CSS” 。請注意,正如我們最初認為的那樣,這並不是指上述關鍵CSS提取。
他們實際做什麼:
使用100%靜態CSS,實際上不會有任何好處。隨著動態頁面在服務器上呈現很少的元素,並且大多數組件在客戶端上動態渲染,因此收益會增加。
例外:使用靜態CSS提取的庫。
了解這些功能如何影響核心的網絡生命力和性能指標是一個非常重要的因素,並且樣式向客戶交付的方式可能會產生最大的影響,因此讓我們詳細分析這一點。
另外,我們需要考慮2種不同的方案:
.js , .css ,媒體等); .css文件提取生成.css靜態文件的解決方案通常將其包含在頁面的<head>中的<link> tag(s),這基本上是渲染阻斷資源。這極大地影響了FCP , LCP和隨後的任何其他指標。
?空緩存
如果用戶有一個空的緩存,則需要發生以下情況,對FCP和LCP產生負面影響:
<body>的任何渲染,即使整個HTML也可以提前提前獲取。的確,您可以在其他其他<head>資源(附加.css或.js文件)中獲取,但這通常是一個不好的做法。
?完整的緩存
但是,在隨後的訪問中,整個.css資源將被緩存,因此FCP和LCP將受到積極影響。
關鍵點
當以下內容時,該解決方案似乎更適合
.css文件,該文件在訪問其他頁面時可能會被緩存;<style>標籤注入樣式在SSR期間,樣式將在頁面的<head> >中添加為<style> >標籤。請記住,這些通常不包含頁面所需的所有樣式,因為大多數庫執行關鍵的CSS提取,因此這些styles通常應比以前討論的整個.css靜態文件小。
?空緩存
因為我們將少量的CSS字節運送,並且它們在.html文件中內襯,因此這將導致FCP和LCP更快:
.css文件的其他請求,因此瀏覽器不會被阻止;.js文件請求移至文檔末尾,則<head>將不會執行任何請求,因此渲染將超快發生;.css提取不需要:.js文件與組件一起捆綁在一起(其中包括所有已經在<style> tag +其他的關鍵CSS); ?完整的緩存
當用戶的緩存已滿時,額外的.js文件將不需要獲取,因為它們已經被緩存。
但是,如果該頁面被劃分,則將在文檔的<style>標籤中渲染的夾具關鍵的CSS再次下載,除非我們處理也可以緩存的靜態HTML,否則我們可以處理基礎架構上的HTML緩存。
但是,默認情況下,無論是否緩存,我們都會在HTTP請求上運送額外的字節。
關鍵點
當以下內容時,該解決方案似乎更適合
大多數解決方案都說它們刪除了未使用的代碼/樣式。這只是一半。
確實很難積累未使用的代碼,尤其是如果將其與過去十年前寫的普通.css文件進行比較。但是,與CSS模塊相比,差異並不大。提供定義任意選擇器或嵌套樣式的選項的任何解決方案都將捆綁它們,無論它們是否在我們的組件內使用。我們設法將未使用的SSR樣式運送出所有經過測試的解決方案。
很難實現真實和完整未使用的代碼刪除,因為CSS語法沒有類型檢查,也不可靜態分析。同樣,組件的動態性質使得在某些情況下實際上是不可能的,尤其是當標記被動態渲染時:
& span :後代元素;&:nth-child() :某些偽選擇器;& .bg-${color} :動態選擇器;.parent &基本上,當我們刪除組件時,我們得到的是刪除代碼,否則我們不再導入它。這是隱式的行為,因為樣式是組件的直接依賴性。當組件消失時,其樣式也是如此。
有兩種方法可以將CSS注入DOM並從JavaScript進行更新:
<style>標籤這種方法意味著在DOM中添加一個或多個<style>標籤(在<head>中或在<body>中的某個地方),使用.appendChild()添加<style> node(s),此外dextContent,.innerhtml,.innerhtml以更新<style> style> s s)。
<style>標籤並更新其整個內容,當我們實際上僅更改一小部分CSS規則時,更新整個DOM可能會很慢。DEVELOPMENT模式下使用此解決方案,因為它提供了更好的調試體驗;PRODUCTION ; CSSStyleSheet API該方法首先使用JSS使用,使用CSSStyleSheet.insertRule()將CSS規則直接注入CSSOM 。
<style> tag;<style>標籤。$0獲取訪問它(或使用DOM API以任何其他方式獲得參考);<style>標籤上的access .sheet.cssRules查看其中包含的CSS規則;PRODUCTION中使用此方法。DEVELOPMENT模式; 如果相同的組件是通過2種不同的路由導入的,則將兩次發送給客戶端。在我們的情況下,這無疑是捆綁器/構建系統的限制。
在Next.js中,在路線級別上進行代碼拆分作品,將特定路線所需的所有組件捆綁在一起,但是根據他們的官方博客和Web.dev,如果在超過50%的頁面中使用了一個組件,則應包含在commons Bundle中。但是,在我們的示例中,我們有2頁,每個頁面都會導入Button組件,並且它包含在每個頁面捆綁包中,而不是在commons Bundle中。由於樣式所需的代碼與組件捆綁在一起,因此此限制也會影響樣式,因此值得記住。
這是一種良好,成熟和紮實的方法。毫無疑問,這是對BEM,SMACC,OOCS或任何其他可擴展的CSS方法的巨大改進,以結構和組織我們的CSS,尤其是在基於組件的應用中。
於2015年推出|返回概述
✅上下文感知代碼完成
✅框架不可知論
沒有樣式/組件共同設置
沒有打字稿支持
沒有原子CSS
沒有主題支持
樣式定義方法
樣式嵌套
樣式應用方法
classNamestyled組件css道具樣式輸出
.css文件提取<style>標籤注射在比較所有以下CSS-IN-JS解決方案時,這是我們考慮的基線。結帳,以更好地了解我們要填寫的這種方法的局限性。
| 轉移 / gzz | 未壓縮 | |
|---|---|---|
| 索引頁面大小 | 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
非常簡單的解決方案,沒有專用網站用於文檔,所有內容都在Github上。它不受歡迎,但它是Next.js中的內置解決方案。
版本: 4.0 |由Vercel維護|於2017年推出|查看文檔| ...返回概述
✅樣式/組件共處
?上下文感知的代碼完成:要獲得語法突出顯示和代碼完成,需要編輯器擴展程序
?打字稿支持: @types可以安裝額外,但是API太小了,不需要TS
沒有原子CSS
沒有主題支持
不是框架不可知
樣式定義方法
樣式嵌套
樣式應用方法
classNamestyled組件css道具樣式輸出
.css文件提取<style>標籤注射elements ,並且它為它們生成了獨特的類名稱(不確定這是一個好練習)總體而言,我們感覺就像寫普通CSS一樣,具有能夠與組件一起定義樣式的額外好處,因此我們不需要其他.css文件。確實,這是庫的理念:支持組件文件中的CSS語法。我們可以使用帶有字符串插值的功能的任何JS/TS常數。使用動態樣式非常容易,因為它最終是簡單的JavaScript。我們以非常低的價格獲得所有這些好處,開銷很小。
缺點是寫普通CSS的總體經驗。不嵌套支持偽類的類別/元素和媒體查詢變得非常笨拙。
| 轉移 / gzz | 未壓縮 | |
|---|---|---|
| 索引頁面大小 | 79.5 kb | 245 kb |
| vs. 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
可以肯定的是最受歡迎和最成熟的解決方案之一,具有良好的文檔。它使用標記模板默認定義樣式,但也可以使用對象。它還普及了styled組件方法,該方法與定義的樣式一起創建了新組件。
版本: 5.3 |由Max Stoiber等維護|於2016年推出|查看文檔| ...返回概述
✅樣式/組件共處
✅打字稿支持: @types必須通過肯定地安裝
✅內置主題支持
✅框架不可知論
?上下文感知的代碼完成:需要編輯器擴展程序/插件
沒有原子CSS
樣式定義方法
樣式嵌套
樣式應用方法
classNamestyled組件css道具樣式輸出
.css文件提取<style>標籤注射Props變化的動態樣式混合等)樣式組件使用styled方法提供了一種新型的樣式組件方法,該方法創建了包括定義樣式的新組件。我們不想寫CSS,因此來自CSS模塊,我們必須學習一種新的,更具程序性的方式來定義樣式。因為它允許string和object語法,所以它是一個非常撓性的解決方案,既可以遷移我們的現有樣式,又用於從頭開始啟動項目。此外,維護者在與該領域的大多數創新保持一致方面做得很好。
但是,在採用它之前,我們必須意識到,它的捆綁尺寸有一定的成本。
| 轉移 / gzz | 未壓縮 | |
|---|---|---|
| 索引頁面大小 | 90.1 kb | 272 kb |
| vs. 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
可能是最全面,最完整和統一的解決方案。詳細的文檔完整地使用打字稿構建,看起來很成熟,具有豐富的功能且維護良好。
版本: 11.4 |由米切爾·漢密爾頓(Mitchell Hamilton)及其他人維護|於2017年推出|查看文檔| ...返回概述
✅樣式/組件共處
✅打字稿支持
✅內置主題支持
✅上下文感知的代碼完成:對於使用styled組件方法,需要附加編輯器插件
✅框架不可知論
沒有原子CSS
樣式定義方法
樣式嵌套
樣式應用方法
className (使用 @emotion/css)styled組件css道具樣式輸出
.css文件提取<style>標籤注射css Prop在開發過程中提供了出色的人體工程學,但是它似乎是一種較新的方法,基於React 17新的jsx變換,並且配置它並不瑣碎,在您的設置上有所不同,並暗示了某些樣板(這應該很快變化並變得更加容易) styled方法將在我們的捆綁包中添加3 kB ,因為它是從單獨的包裝中導入的css Prop& styled組件) 總體情緒看起來是一種非常紮實和靈活的方法。新穎的css道具方法為開發人員提供了出色的人體工程學。使用動態樣式和打字稿非常容易且直觀。在定義樣式時支持strings和objects ,從普通CSS遷移或從頭開始時可以很容易地使用它。捆綁開銷不是可以忽略不計,但絕對比其他解決方案小得多,尤其是如果您考慮其提供的豐富功能。
看來它沒有專門關注性能,而是更多地關注開發人員的體驗。它看起來像是一個完美的“全面”解決方案。
| 轉移 / gzz | 未壓縮 | |
|---|---|---|
| 索引頁面大小 | 83.2 kb | 253 kb |
| vs. 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
最少的庫,僅專注於類型檢查。這是不可知論的框架,這就是為什麼它沒有用於處理動態樣式的特殊API的原因。 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: