Apogée Consult
Retour au blog
Mathieu Ponton
Mathieu PontonCo-Founder & ingénieur logiciel

Je suis Mathieu Ponton, Co-Founder & ingénieur logiciel chez Apogée Consult à Lyon. Ingénieur diplômé de Polytech Lyon (Informatique), j'ai fait trois ans en apprentissage, partagés entre la Métropole de Lyon (inclusion numérique avec Res'in et sobriété énergétique avec Écolyo) et Superwyze, une startup medtech (POCs, dont certains aujourd'hui industrialisés, et travail sur des codebases existantes). J'ai livré plus de 10 projets en production (web, mobile et IA / RAG) pour des PME, startups et organisations publiques.

Tailwind v4 et bundle CSS : ce qui change vraiment pour la production

  • perf

Tailwind v4 repose sur un nouveau moteur Oxide en Rust. Voici ce que ça change concrètement sur le bundle CSS en production, sans la brochure marketing.

Tailwind v4 et bundle CSS : ce qui change vraiment pour la production

Tailwind v4 ne se résume pas à un changement de syntaxe. Le moteur qui génère le CSS a été réécrit en Rust, c'est Oxide. Ce moteur change le moment où le CSS est produit, comment il est scanné, et ce que vous obtenez en sortie. Avant de migrer, il vaut mieux comprendre les effets réels sur la production plutôt que de s'arrêter au gain de vitesse en dev.

Ce qu'Oxide change fondamentalement

Dans Tailwind v3, le moteur analysait un ensemble de fichiers selon les globs définis dans content. À chaque build, il parcourait ces fichiers, extrayait les classes, et générait le CSS correspondant.

Oxide ne parcourt plus des globs. Il analyse directement le graphe d'imports de votre projet. Cela signifie qu'un fichier non importé, même s'il correspond à un glob, n'est pas scanné.

En pratique, cela a deux effets opposés selon votre architecture :

  • Side-effect positif : les classes mortes dans des fichiers jamais importés disparaissent automatiquement du bundle.
  • Side-effect négatif : les classes chargées dynamiquement depuis des sources non importées (templates d'email séparés, fichiers générés, contenu CMS) ne sont plus détectées. Vous devez les déclarer explicitement via @source.

La taille du bundle CSS en production

Le CSS généré par Tailwind v4 est sensiblement différent de celui de v3, mais pas pour les raisons qu'on imagine.

Tailwind v3 produisait un reset (Preflight) volumineux, suivi des utilities générées. En v4, le reset est restructuré autour des couches CSS (@layer base, @layer components, @layer utilities) et plusieurs utilities anciennement séparées sont fusionnées.

Voici un ordre de grandeur sur un projet Next.js de taille moyenne (50 composants, design system interne) :

  • Tailwind v3 : CSS final après PurgeCSS/content scan, environ 12 à 18 ko gzippé
  • Tailwind v4 même projet migré : entre 9 et 14 ko gzippé

La réduction vient surtout de la suppression automatique des classes non utilisées grâce au scan par imports, et d'une consolidation de certaines utilities (notamment les variantes de hover, focus, dark mode).

Ce n'est pas un gain spectaculaire, mais c'est cohérent avec ce qu'on observe sur nos projets internes.

La configuration : plus de tailwind.config.js

C'est le changement le plus visible. La configuration passe dans le CSS lui-même :

@import "tailwindcss";

@theme {
  --color-brand: #044477;
  --font-sans: "Inter", sans-serif;
  --radius-card: 0.75rem;
}

@source "./src/templates/email/**/*.html";

Le bloc @theme remplace theme.extend. Le bloc @source remplace les entrées content. Cette approche évite la duplication entre le fichier JS et le CSS, mais casse tous les outils qui lisent tailwind.config.js, notamment certains plugins Prettier, les presets partagés en monorepo, et quelques wrappers de storybook.

Si vous travaillez en monorepo avec un preset Tailwind partagé, la migration est non triviale. Tailwind v4 supporte les presets via @plugin, mais le format a changé.

Le scan par imports et les classes dynamiques

C'est le point qui génère le plus de bugs silencieux en migration.

Avec Tailwind v3, vous pouviez écrire :

const color = condition ? "text-red-500" : "text-green-500";

...et si ces fichiers étaient dans le glob content, les classes étaient incluses. En v4, si Tailwind ne détecte pas ces chaînes lors du scan d'imports (notamment quand elles viennent d'une variable externe, d'une API, ou d'un objet de mapping), elles disparaissent du CSS final.

La règle est stricte : toute classe doit apparaître sous forme de chaîne littérale complète quelque part dans le graphe d'imports scanné. Si vous construisez des classes dynamiquement (bg-${color}-500), elles ne seront pas incluses, en v3 non plus en théorie, mais certains patterns passaient par accident.

La solution standard reste la safelist, désormais déclarée via @source inline(...) :

@source inline("bg-red-500 bg-green-500 bg-blue-500");

Ce qui ne change pas

Le modèle JIT reste le même, le CSS n'est pas généré à la demande au runtime, il est statique. Tailwind v4 ne fait pas de CSS-in-JS, pas d'injection runtime.

Les utilitaires de base (flex, grid, p-4, text-sm) fonctionnent identiquement. La syntaxe des variantes évolue légèrement (notamment pour les variantes arbitraires et les modificateurs de groupe), mais le comportement est identique.

La compatibilité avec PostCSS reste assurée. Tailwind v4 fonctionne comme plugin PostCSS, ce qui préserve l'intégration avec Next.js, Vite, et les pipelines CI/CD existants.

Ce qu'on recommande avant de migrer

Trois vérifications préalables :

  1. Auditer les classes dynamiques. Cherchez tous les patterns className={...} qui construisent des chaînes à partir de variables. Ce sont des candidats à la rupture.
  2. Vérifier les sources hors graphe d'imports. Templates d'email, CMS headless qui renvoie des classes, fichiers JSON de configuration UI, tout ce qui n'est pas importé via ESM doit être listé dans @source.
  3. Tester les presets et plugins Tailwind existants. Certains plugins v3 ne sont pas compatibles v4. Vérifiez la feuille de route officielle.

La migration progressive n'est pas possible en l'état : Tailwind v4 nécessite un changement complet du fichier de configuration. Un outil de codemod officiel existe (npx @tailwindcss/upgrade), mais il ne couvre pas les cas de presets complexes ni les @apply imbriqués dans des fichiers SCSS.

L'Oxide engine apporte un gain de vitesse réel en développement, les premières mesures publiées par l'équipe Tailwind évoquent un facteur 10 sur des projets de taille moyenne. En production, le gain sur le bundle reste modeste et dépend fortement de votre architecture. Ce n'est pas une raison de ne pas migrer, mais ce n'est pas non plus la raison principale de le faire.

Disponible pour de nouveaux projets

Un projet à concrétiser ?
Parlons-en, sans engagement.

Un échange de 30 minutes pour cadrer votre besoin, qualifier la faisabilité et vous proposer une trajectoire claire.

1// kick-off : réponse sous 24h
2const project = await apogee.scope({
3 type: 'web | mobile | IA',
4 timeline: '6 à 16 semaines',
5 approach: 'sur-mesure'
6})
7// → cadrage offert
Tailwind v4 performance production : ce qui change | Apogée Consult