Panda CSS vs StyleX
Comparez Panda CSS et StyleX : découvrez comment les tokens, la prévention des conflits de styles, le build, les thèmes et les valeurs dynamiques guident votre choix.
Panda CSS et StyleX compilent tous deux les styles en CSS atomique statique au moment du build et n’envoient aucun runtime de stylisation au navigateur. Ce qui les distingue, c’est le problème que chacun a été conçu pour résoudre : Panda impose un design system, tandis que StyleX empêche les styles d’un composant d’entrer en collision avec ceux d’un autre.
Les deux figurent sur la liste restreinte dès qu’une équipe souhaite abandonner styled-components ou Emotion sans renoncer à des styles typés et colocalisés, et leurs README se ressemblent presque mot pour mot. Cet article expose l’unique différence structurelle entre eux, présente le même composant Button écrit avec chacun, puis montre comment cette différence se répercute sur le workflow, le theming et les valeurs dynamiques. Pour une vue d’ensemble, consultez l’état du CSS-in-JS en 2026.
Points clés à retenir
- Panda CSS et StyleX extraient tous deux les styles au moment du build sous forme de CSS atomique : aucun des deux n’injecte de styles dans le navigateur pendant le rendu.
- Panda fait des design tokens définis dans
panda.config.tsla source de vérité et en génère des helpers typéscss(),cva()etstyled(). - Avec StyleX, chaque composant définit ses styles via
stylex.create(), etstylex.props()les fusionne de sorte que, pour une propriété donnée, le dernier style passé l’emporte. - Aucune des deux bibliothèques ne peut transformer en classe atomique une valeur qui n’existe qu’à l’exécution. StyleX fait transiter ces valeurs par des variables CSS, tandis que la documentation de Panda oriente vers les recipes, les variables CSS ou les attributs data.
- Panda convient à une équipe qui s’éloigne de son design system, StyleX à une large base de code où les fuites de styles constituent le bug récurrent, et de nombreux projets n’ont besoin ni de l’un ni de l’autre.
Ce que Panda CSS et StyleX ont en commun
Panda CSS et StyleX sont deux bibliothèques CSS-in-JS zero-runtime. Vous écrivez des objets de style en TypeScript, une étape de build les extrait, et le navigateur reçoit une simple feuille de style composée de classes atomiques à usage unique. Les bibliothèques à runtime comme styled-components et Emotion fonctionnent différemment : elles génèrent et injectent les styles via JavaScript pendant le rendu. Panda et StyleX effectuent tout ce travail avant la mise en production du code. Si vous souhaitez découvrir la version plus ancienne de ce même clivage, l’article Linaria vs styled-components la traite en détail.
Comme le résultat est du CSS statique, aucune des deux bibliothèques ne nécessite de context provider ni d’injecteur de styles au rendu. C’est pourquoi toutes deux fonctionnent avec les React Server Components : aucun code de stylisation ne s’exécute lors d’un rendu serveur ou client. Dans une sortie atomique, chaque classe définit une seule propriété : la taille de la feuille de style croît donc avec le nombre de déclarations uniques, et non avec le nombre de composants.
Panda CSS : le design system d’abord
Panda CSS considère le design system comme la source de vérité. Les tokens définis dans panda.config.ts alimentent une étape de génération de code qui produit des helpers typés css(), cva() et styled() pour le reste de la base de code. Panda est issu de l’équipe à l’origine de Chakra UI, et cet héritage de bibliothèque de composants se ressent.
L’exécution de la commande panda codegen écrit un répertoire styled-system/ dans votre projet. Les composants importent leurs helpers depuis ce répertoire, et les types qu’il contient sont générés à partir de vos tokens. Si brand est un token de couleur, bg: 'brand' bénéficie de l’autocomplétion. Avec l’option de configuration strictTokens, une valeur hexadécimale brute devient une erreur de type.
Les recipes transforment ce principe en API de composants. cva() prend un style base, des variants nommés et des defaultVariants, et renvoie une fonction qui associe les props de variante à des noms de classe. Le design system décide des options disponibles, et les composants choisissent parmi elles.
StyleX : les collisions d’abord
StyleX considère les collisions de styles comme le problème central. Chaque composant définit ses propres objets de style avec stylex.create(), et stylex.props() les fusionne de manière déterministe : lorsque deux styles définissent la même propriété, le dernier passé l’emporte. L’ordre de la feuille de style et la spécificité des sélecteurs n’entrent pas en ligne de compte. StyleX est un projet open source de Meta, et sert de couche de stylisation à des design systems tels qu’Astryx.
const styles = stylex.create({
a: { color: 'red' },
b: { color: 'blue' },
})
stylex.props(styles.a, styles.b) // blue
stylex.props(styles.b, styles.a) // red
Les règles de fusion de StyleX s’appliquent également aux propriétés raccourcies (shorthands). Thinking in StyleX explique qu’un conflit entre une propriété raccourcie et une propriété détaillée (longhand) se résout toujours de la même manière : par défaut, marginTop prend le pas sur margin, quel que soit celui que vous passez en premier. La compilation est assurée par le plugin Babel de StyleX, sur lequel reposent les intégrations officielles pour les bundlers et PostCSS.
Le même bouton stylé des deux façons
Les composants Button ci-dessous, écrits avec Panda CSS et StyleX, exposent la même prop intent. La différence réside dans l’endroit où se prennent les décisions de style.
// Panda: panda.config.ts defines theme.extend.tokens.colors.brand = { value: '#2563eb' }
import { cva } from '../styled-system/css'
const button = cva({
base: { px: '4', py: '2', borderRadius: 'md', fontWeight: 'semibold' },
variants: {
intent: {
primary: { bg: 'brand', color: 'white' },
ghost: { bg: 'transparent', color: 'brand' },
},
},
defaultVariants: { intent: 'primary' },
})
export function Button({ intent, ...props }) {
return <button className={button({ intent })} {...props} />
}
// StyleX
import * as stylex from '@stylexjs/stylex'
const styles = stylex.create({
base: { paddingInline: 16, paddingBlock: 8, borderRadius: 6, fontWeight: 600 },
primary: { backgroundColor: '#2563eb', color: 'white' },
ghost: { backgroundColor: 'transparent', color: '#2563eb' },
})
export function Button({ intent = 'primary', style, ...props }) {
return <button {...props} {...stylex.props(styles.base, styles[intent], style)} />
}
Dans la version Panda, bg: 'brand' ne passe la vérification de types que parce que brand existe dans la configuration et que le codegen a émis le type correspondant. Dans la version StyleX, chaque variante est un objet distinct, et le style du consommateur est passé en dernier. Partout où il définit la même propriété que la base ou la variante, la valeur du consommateur l’emporte, sans aucune bataille de spécificité. Les helpers de Panda limitent les valeurs qu’un développeur peut utiliser. La règle de fusion de StyleX limite la manière dont les styles d’un composant peuvent écraser ceux d’un autre.
Conséquences pratiques du choix entre Panda CSS et StyleX
Cette différence d’origine se manifeste dans trois domaines du quotidien : le fonctionnement du build, celui du theming, et le traitement des valeurs qui n’existent qu’à l’exécution.
| Critère | Panda CSS | StyleX |
|---|---|---|
| Étape de build | Le codegen écrit styled-system/ ; le plugin PostCSS ou la CLI extrait le CSS | Le plugin Babel compile les appels à stylex.create |
| Theming | Tokens dans la configuration | defineVars combiné à createTheme |
| Valeurs à l’exécution | Non extractibles ; recourir aux recipes, aux variables CSS ou aux attributs data | Acheminées via des variables CSS |
Workflow : codegen vs plugin de compilation
Avec Panda, un artefact généré s’intercale entre votre configuration et vos composants : modifier un token implique donc de régénérer styled-system/. StyleX ne possède pas de répertoire généré. Le plugin Babel transforme chaque module à l’endroit où il est écrit, et la configuration du plugin détermine la résolution des imports et des variables.
Theming : tokens vs variables
Avec Panda, le thème correspond à l’ensemble des tokens : modifiez brand dans la configuration, et toutes ses utilisations changent au build suivant. Avec StyleX, le theming est un mécanisme de surcharge qui vient s’ajouter aux styles propres à chaque composant. stylex.defineVars déclare des variables dans un fichier .stylex.ts, et stylex.createTheme produit des surcharges que vous appliquez à un élément ancêtre :
// tokens.stylex.ts
import * as stylex from '@stylexjs/stylex'
export const colors = stylex.defineVars({ brand: '#2563eb' })
// app.tsx
import * as stylex from '@stylexjs/stylex'
import { colors } from './tokens.stylex'
const dark = stylex.createTheme(colors, { brand: '#60a5fa' })
<div {...stylex.props(dark)}>{children}</div>
Les API de theming de StyleX ne fonctionnent qu’une fois l’option unstable_moduleResolution du plugin Babel configurée.
Valeurs dynamiques : là où le zero-runtime devient délicat
Un extracteur au moment du build ne voit que les valeurs écrites dans le code source. Une valeur connue uniquement à l’exécution, comme une couleur choisie par l’utilisateur, ne peut devenir une classe atomique dans aucune des deux bibliothèques. Le guide sur le style dynamique de Panda explique que son extracteur ignore tout ce qu’il ne peut pas lire dans le code source, et oriente vers les recipes, les variables CSS ou les attributs data-*. StyleX gère les styles dynamiques différemment : le compilateur émet une classe fixe qui pointe vers une variable CSS, et la valeur d’exécution est écrite dans cette variable sur l’élément.
Panda CSS vs StyleX : quel est votre problème ?
Choisissez en fonction du problème que vous rencontrez, et non de la liste des fonctionnalités. Une petite équipe dont l’interface ne cesse de s’écarter de son design system a précisément le problème pour lequel Panda a été conçu : les tokens deviennent des types, et les valeurs hors système ne compilent plus. Une large base de code où les styles d’un composant ne cessent de déborder sur un autre a précisément le problème pour lequel StyleX a été conçu : chaque composant possède ses propres styles, et l’ordre de fusion au point d’appel détermine le résultat.
De nombreux projets n’ont ni l’un ni l’autre de ces problèmes. Pour eux, les CSS Modules ou un framework utilitaire font l’affaire avec moins d’outillage de build que n’en exige l’une ou l’autre de ces bibliothèques. Pour une comparaison de deux autres approches de stylisation, consultez Tailwind vs Sass.
Avant de vous engager avec l’une ou l’autre bibliothèque, identifiez le bug de style que votre équipe ne cesse de corriger. S’il s’agit d’une dérive par rapport au design system, prototypez une recipe avec Panda. S’il s’agit de conflits de surcharge, portez un composant partagé vers StyleX et observez le comportement de ses consommateurs.
FAQ
En quoi les états de survol et les media queries diffèrent-ils entre Panda CSS et StyleX ?
Dans StyleX, les pseudo-classes et les media queries se placent dans la valeur d'une propriété sous la forme d'un objet de conditions, et cet objet doit comporter une entrée default, que vous pouvez définir à null. Les pseudo-éléments comme ::placeholder font exception : ils s'écrivent sous forme de clé propre au niveau supérieur du namespace. Panda accepte les conditions à l'intérieur d'une propriété, comme dans bg: { base: 'red.500', _hover: 'red.700' }, ou sous forme d'objets de condition, en utilisant des noms intégrés tels que _hover et des clés de breakpoint mobile-first telles que md et lg.
StyleX peut-il styliser les éléments enfants comme le permettent les sélecteurs arbitraires de Panda ?
Pas directement. StyleX interdit les sélecteurs arbitraires et la stylisation à distance, alors que css() de Panda accepte des sélecteurs arbitraires comme '& > *'. StyleX 0.16.0 a introduit les API stylex.when (ancestor, descendant, anySibling, siblingBefore et siblingAfter). Vous marquez un élément avec stylex.defaultMarker(), et un autre élément modifie ses styles lorsque l'élément marqué est survolé, reçoit le focus ou correspond à un attribut. Les variantes descendant, anySibling et siblingAfter reposent sur le sélecteur CSS :has(). Définir une variable defineVars sur un parent constitue une autre approche documentée.
stylex.props() s'exécute-t-il dans le navigateur ?
Parfois, et uniquement sous la forme d'une petite étape de fusion. Le compilateur StyleX transforme les appels à stylex.create() en CSS atomique statique au moment du build, et lorsque les styles sont créés et utilisés dans le même fichier, il peut supprimer entièrement l'appel à stylex.props(). Lorsque les styles traversent les frontières entre fichiers, stylex.props() s'exécute comme une fonction qui fusionne les objets de style compilés de gauche à droite et renvoie les props className et style pour React. Elle ne génère ni n'injecte jamais de CSS dans le navigateur. Pour les frameworks autres que React, comme Solid, Svelte, Qwik et Vue, StyleX fournit également stylex.attrs().
Quelle est la différence entre strictTokens et strictPropertyValues dans Panda CSS ?
strictTokens concerne les propriétés adossées à des tokens, comme color, bg et borderColor : lorsque cette option est activée, seules les valeurs de tokens y sont acceptées. strictPropertyValues concerne une liste fixe de propriétés aux valeurs de mots-clés connues, comme display, position et overflow : lorsque cette option est activée, Panda lève une erreur si une valeur ne fait pas partie de ces mots-clés. Les deux s'activent dans panda.config.ts. Dans les deux cas, encadrer une valeur brute de crochets, comme dans [123px], permet de la faire passer sans erreur TypeScript.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k