Changer le thème

Optimisation des performances Tailwind : JIT, configuration content et contrôle du volume en production

Easton editorial illustration: signal tracing instrument

Ouvrez Chrome DevTools et regardez ce fichier CSS de 3,5 Mo.

Une page de nouvelle fonctionnalité vient d’être mise en ligne ; le temps de chargement est passé de 800 ms à 3,2 secondes. Après un tour de diagnostic, le problème venait bien de ce fichier CSS — rempli de classes Tailwind jamais utilisées.

Tailwind se vend comme « performant », comment peut-il ralentir le site ?

Finalement, le problème n’était pas Tailwind, mais la configuration. Une fois le fonctionnement du mode JIT compris, la config content ajustée, et quelques optimisations de build production appliquées, le CSS passe du Mo au Ko — le site de Netflix n’utilise plus que 6,5 Ko.

Aujourd’hui, je partage les pièges que j’ai rencontrés.


I. Mode JIT : la révolution performance de Tailwind

1.1 Les limites du mode traditionnel

Avant de voir ce fichier CSS de 3,5 Mo, je voyais Tailwind surtout comme un « framework CSS utility-first ».

Le « mode traditionnel » d’avant Tailwind v2 pré-générait toutes les combinaisons de classes possibles — toutes les couleurs, tous les espacements, toutes les variantes (hover, focus, disabled, etc.). Sur un projet moyennement complexe, le CSS de développement pouvait dépasser 10 Mo.

10 Mo+
Volume CSS en développement (mode traditionnel)

En local, 10 Mo de CSS ne choquent pas — la bande passante n’est pas le problème. Mais le navigateur doit parser une feuille aussi lourde ; la mémoire et les perfs DevTools en souffrent.

En déboguant dans Firefox, j’ai déjà eu des freezes de plusieurs secondes après un changement de classe ; le rechargement de page devenait pénible.

Pire encore : en mode traditionnel, on hésite à toucher la config. Nouveau breakpoint ou variante focus-visible ? Il faut d’abord estimer « combien de combinaisons en plus ». L’équipe finit par choisir entre performance et flexibilité.

1.2 Comment fonctionne le JIT

Le mode JIT (Just-in-Time), introduit en Tailwind v2.1 et activé par défaut en v3+, repose sur une idée simple : génération à la demande.

Le mode traditionnel pré-génère toutes les combinaisons possibles, même inutilisées. Le JIT fait l’inverse — il scanne vos templates (HTML, JSX, Vue, etc.), repère les classes réellement utilisées, puis ne génère que ces styles.

Exemple concret. En mode traditionnel, Tailwind produit du CSS comme :

.bg-black { background-color: #000 }
.hover\:bg-black:hover { background-color: #000 }
.focus\:bg-black:focus { background-color: #000 }
.disabled\:bg-black:disabled { background-color: #000 }
/* ... des dizaines d'autres combinaisons de variantes */

Même si le projet n’utilise qu’un seul bg-black, toutes les autres variantes sont générées.

Avec le JIT, c’est différent. Il scanne les templates, voit seulement bg-black et hover:bg-black, et ne génère que :

.bg-black { background-color: #000 }
.hover\:bg-black:hover { background-color: #000 }

Résultat : le CSS de développement passe de 10 Mo à quelques Ko — le même ordre de grandeur qu’en production.

1.3 Gains concrets du JIT

Je me souviens de ma première activation du JIT : le rechargement était si rapide que ça semblait irréel — plusieurs secondes avant, quasi instantané après.

Vitesse de build : un build complet prenait 2-3 minutes ; maintenant quelques secondes. Le JIT n’a plus besoin de pré-générer tous les styles, il suffit de scanner les templates et d’extraire les classes.

Expérience de développement : DevTools ne freeze plus. Avant, changer une classe forçait le navigateur à reparser 10 Mo de styles ; avec quelques Ko, la mise à jour est quasi immédiate.

Valeurs arbitraires : bonus inattendu. En mode traditionnel, pour text-[#facc15], il fallait souvent une safelist. Le JIT l’accepte directement :

// Pas besoin de pré-définir dans la config, utilisez directement
<h1 class="text-[2.5rem] mt-[1.35rem] text-[#facc15]">
  Le JIT simplifie tout
</h1>

Classes dynamiques : en mode traditionnel, 'text-' + color n’est pas détecté. Le JIT a aussi ses limites, mais avec safelist on gère mieux les cas dynamiques.

Vous vous demandez peut-être comment activer le JIT. Aujourd’hui (Tailwind v3+), c’est le défaut, sans config supplémentaire. Sur v2 :

// tailwind.config.js
module.exports = {
  mode: 'jit',  // v2 : activation manuelle
  content: ['./src/**/*.{html,js,jsx,ts,tsx}'],
  // ...
}

II. Configuration content : la clé d’un scan précis

Le JIT est efficace seulement si content est bien configuré.

content indique quels fichiers Tailwind scanne pour extraire les classes. Mal réglé : styles manquants (portée trop étroite) ou CSS gonflé (portée trop large).

2.1 Bases de la config content

La syntaxe de base :

// tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{html,js,jsx,ts,tsx}',  // tous les templates sous src
  ],
  // ...
}

Ici, ** signifie n’importe quel niveau de répertoire ; *.{html,js,jsx,ts,tsx} filtre par extension.

Selon le framework, adaptez les chemins :

// Projet Next.js
content: [
  './pages/**/*.{js,ts,jsx,tsx}',
  './components/**/*.{js,ts,jsx,tsx}',
  './app/**/*.{js,ts,jsx,tsx}',  // App Router
]

// Projet Astro
content: [
  './src/**/*.{astro,html,js,jsx,ts,tsx}',
]

L’essentiel : couvrir tous les fichiers qui utilisent des classes Tailwind. Omettre un répertoire, et les styles de ce dossier ne seront pas générés.

2.2 Pièges que j’ai rencontrés

Piège 1 : glob trop large

Pour « simplifier », j’avais écrit :

content: [
  './**/*.js',  // scanne node_modules !
]

Tailwind a parcouru tous les JS de node_modules ; le build a explosé, avec des styles absurdes générés en plus.

Limitez la portée :

content: [
  './src/**/*.js',      // src uniquement
  './components/**/*.js', // components uniquement
]

Piège 2 : répertoire de composants oublié

Après une refacto, les composants étaient dans ./lib/components/. J’avais oublié de mettre à jour content ; les styles des nouveaux emplacements avaient disparu.

La leçon : à chaque changement de structure, synchronisez content.

Piège 3 : classes construites dynamiquement

Ce genre de code :

const color = 'red';
const className = `text-$&#123;color&#125;-500`;  // non détecté par le JIT

Le JIT voit une chaîne template, pas un nom de classe complet. En production, le style disparaît.

Solution : safelist (voir plus bas) ou syntaxe par objet :

const colors = {
  red: 'text-red-500',
  blue: 'text-blue-500',
};
const className = colors[color];  // nom complet, détectable

2.3 safelist et classes dynamiques

Quand les classes dynamiques sont inévitables, safelist entre en jeu.

// tailwind.config.js
module.exports = {
  safelist: [
    'text-red-500',
    'text-blue-500',
    'bg-red-500',
    // ou regex pour un groupe de classes
    {
      pattern: /text-(red|blue|green)-(500|600)/,
      variants: ['hover', 'focus'],  // conserver les variantes
    },
  ],
}

safelist force Tailwind à générer ces styles même s’ils n’apparaissent pas tels quels dans les templates.

Attention : plus la safelist est longue, plus le CSS grossit. Réservez-la aux cas nécessaires ; ne la transformez pas en « coffre-fort » pour toutes les classes possibles.


III. Contrôle du volume en production : stratégie en quatre couches

Le JIT rend le CSS léger en dev, mais la production mérite encore plus d’optimisation.

Voici une stratégie en quatre couches, de la config à la compression.

Couche 1
Config content précise
Scanner uniquement les fichiers réellement utilisés
Couche 2
Suppression PurgeCSS
Supprimer automatiquement les styles inutilisés
Couche 3
Minify cssnano
Compression CSS
Couche 4
Brotli/Gzip
Compression du transfert réseau
Source: Niveaux d’optimisation

3.1 Couche 1 : config content précise

C’est la base, déjà couverte.

Principe : ne scanner que les fichiers qui utilisent Tailwind. Plus la portée est précise, plus le build est rapide et le CSS petit.

// Bon : portée précise
content: [
  './src/components/**/*.jsx',
  './src/pages/**/*.tsx',
]

// Mauvais : portée trop large
content: [
  './**/*.js',  // scanne node_modules
]

3.2 Couche 2 : suppression automatique PurgeCSS

Tailwind v3+ active PurgeCSS en build de production pour retirer les styles inutilisés.

Assurez-vous que les commandes distinguent dev et production :

# Build dev (ne supprime pas les styles inutilisés)
npm run dev

# Build production (PurgeCSS automatique)
npm run build

Avec PostCSS, contrôle explicite possible :

// postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
    ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}),
  },
}

3.3 Couche 3 : Minify cssnano

Après PurgeCSS, cssnano compresse encore le CSS.

Compression : suppression des commentaires, fusion des règles dupliquées, simplification des sélecteurs, réduction des valeurs, etc.

# Minify direct avec Tailwind CLI
npx tailwindcss -i ./src/input.css -o ./dist/output.css --minify

# ou via PostCSS (voir ci-dessus)

Chiffre concret : mon projet est passé de 150 Ko (après PurgeCSS) à 45 Ko (après minify).

3.4 Couche 4 : compression réseau Brotli/Gzip

Ce n’est pas l’optimisation du CSS lui-même, mais du transfert.

Brotli ou Gzip sur les assets statiques réduit le CSS de 60 à 80 %.

# Brotli dans Nginx (module ngx_brotli requis)
brotli on;
brotli_comp_level 6;
brotli_types text/css application/javascript;

# ou Gzip (support natif Nginx)
gzip on;
gzip_comp_level 6;
gzip_types text/css application/javascript;
6,5 Ko
Volume de transfert CSS Netflix

Comparaison : un CSS de 45 Ko tombe à environ 8 Ko en transfert Brotli.

Quand Netflix annonce 6,5 Ko de CSS, il s’agit du volume transmis après Brotli, pas de la taille du fichier brut.


IV. Cas pratique et comparaison de données

4.1 Avant / après optimisation

3,5 Mo → 28 Ko
CSS build dev
Avant / après activation JIT
320 Ko → 48 Ko
CSS build production
Volume non compressé
7,2 Ko
Volume final transmis
Après compression Brotli
3,2 s → 820 ms
Lighthouse LCP
Performance de chargement
Source: Données mesurées

Franchement, ces chiffres m’ont surpris.

4.2 Dépannage des problèmes courants

Styles manquants ?

Étape 1 : vérifiez content — tous les fichiers avec classes Tailwind doivent être couverts.

Étape 2 : classes dynamiques ? Ajoutez safelist si besoin.

Étape 3 : confirmez un build production (npm run build), pas dev.

CSS encore trop lourd ?

Vérifiez une safelist trop longue, du CSS tiers mélangé au build Tailwind, ou une portée content trop large.

DevTools qui rame ?

Confirmez Tailwind ≥3 (JIT par défaut). Affinez content. Sur v2, migrez ou activez JIT manuellement.


V. Nouveautés Tailwind v4

Après l’existant, regardons Tailwind v4 (fin 2024).

5.1 Moteur Oxide

La mise à jour la plus enthousiasmante : Tailwind a réécrit son moteur en Rust.

182x
Accélération des builds incrémentaux

Donnée officielle : builds incrémentaux 182 fois plus rapides. Changer une classe : secondes avant, millisecondes maintenant.

Oxide met en cache le scan de fichiers et ne retraite que ce qui change. Sur un gros projet (200+ composants), un build de ~10 s devient quasi instantané.

5.2 Objectif zéro configuration

Tailwind v4 pousse l’idée : la plupart des projets n’ont plus besoin de tailwind.config.js.

La config par défaut couvre les besoins courants : CSS moderne, container queries, breakpoints raisonnables, palette complète. Fichier de config seulement pour une personnalisation poussée.

Les nouveaux projets démarrent plus vite, avec moins de risques d’erreur de config.

Pour migrer vers v4 : la syntaxe évolue. Par exemple, theme.extend.colors cède la place aux propriétés CSS personnalisées. Consultez le guide de migration officiel avant de basculer.


Synthèse

Après cette nuit de crise à trois heures du matin, j’ai repassé toute la config Tailwind du projet au peigne fin.

Le JIT aligne dev et prod sur un CSS léger ; content fixe la portée du scan ; quatre couches d’optimisation ramènent le CSS du Mo au Ko. Avec Oxide en v4, la vitesse de build n’est plus un goulot.

Si vous êtes encore en mode traditionnel ou doutez de content, vérifiez d’abord :

  1. Tailwind ≥3 (JIT par défaut)
  2. content couvre précisément tous les templates
  3. build production avec PurgeCSS et cssnano
  4. serveur avec Brotli/Gzip

Après ces ajustements, vous devriez voir un gain net.

Des questions ? Laissez un commentaire, ou parcourez la doc officielle JIT et optimisation production — elle est claire.



Références

Configuration d'optimisation des performances Tailwind CSS

Flux de configuration complet, de l'activation du mode JIT au contrôle du volume en production

⏱️ Estimated time: 30 min

  1. 1

    Step 1: Vérifier la version de Tailwind

    Contrôlez la version de Tailwind CSS du projet :

    • Tailwind v3+ active le mode JIT par défaut
    • Tailwind v2 nécessite d'ajouter manuellement mode: 'jit' dans la config
    • Il est recommandé de migrer vers v3+ pour de meilleures performances
  2. 2

    Step 2: Configurer les chemins de scan content

    Définissez des chemins de scan précis dans tailwind.config.js :

    • Next.js : ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}']
    • Astro : ['./src/**/*.{astro,html,js,jsx,ts,tsx}']
    • Évitez les glob trop larges comme './**/*.js'
  3. 3

    Step 3: Gérer les noms de classes dynamiques

    Pour les classes construites dynamiquement, utilisez safelist pour forcer la génération :

    • Ajoutez les noms de classes complets dans le tableau safelist
    • Utilisez pattern pour faire correspondre un groupe de classes par regex
    • Combinez avec variants pour conserver hover, focus, etc.
  4. 4

    Step 4: Configurer l'optimisation du build de production

    Ajoutez cssnano dans la config PostCSS :

    • Ne pas activer cssnano en développement (build plus rapide)
    • Activer automatiquement cssnano minify en production
    • Utiliser process.env.NODE_ENV pour contrôler l'activation
  5. 5

    Step 5: Configurer la compression serveur

    Configurez Brotli ou Gzip dans Nginx :

    • Brotli : brotli on; brotli_comp_level 6;
    • Gzip : gzip on; gzip_comp_level 6;
    • Inclure les types text/css et application/javascript

FAQ

Comment activer le mode JIT dans Tailwind v3 ?
Tailwind v3+ active le mode JIT par défaut, sans configuration supplémentaire. Si vous utilisez encore Tailwind v2, ajoutez mode: 'jit' dans tailwind.config.js.
Quels problèmes cause l'omission de fichiers dans la config content ?
Omettre des fichiers empêche la génération des styles Tailwind qu'ils contiennent ; après un build de production, les styles disparaissent. Mettez toujours à jour la config content quand la structure du projet change.
Pourquoi les noms de classes dynamiques échappent au JIT ?
Le JIT scanne le texte des fichiers source ; une classe concaténée dynamiquement comme `text-$&#123;color&#125;-500` apparaît comme une chaîne template, pas comme un nom de classe complet, et ne peut pas être détectée. Utilisez safelist ou passez à une syntaxe par mapping d'objets.
Les 6,5 Ko de CSS de Netflix sont-ils le volume brut ou compressé ?
6,5 Ko correspond au volume de transfert réseau après compression Brotli. Le fichier CSS brut fait quelques dizaines de Ko après PurgeCSS et cssnano ; Brotli/Gzip réduit fortement le volume transmis.
Quelles améliorations apporte le moteur Oxide de Tailwind v4 ?
Oxide, réécrit en Rust, accélère les builds incrémentaux de 182 fois. Il met en cache les résultats du scan de fichiers et ne retraite que les parties modifiées ; sur les grands projets, le build passe de l'ordre de 10 secondes à quelques secondes.
Quel impact d'une safelist trop volumineuse ?
safelist force la génération des styles indiqués ; une config trop large gonfle le fichier CSS. Réservez-la aux noms de classes dynamiques vraiment nécessaires, sans y mettre toutes les classes possibles.

9 min de lecture · Publié le: 30 mars 2026 · Mis à jour le: 27 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog