Tailwind v4, c’est le moment où ton vieux réflexe « je vais ouvrir tailwind.config.js et tout régler là-dedans » commence à sonner creux. Pas parce que le fichier a “disparu” (il peut encore exister), mais parce que le centre de gravité a bougé. Tu configures plus en CSS, tu assumes tes tokens comme des variables, et tu laisses Tailwind faire plus de boulot tout seul.
Si tu arrives de Tailwind v3 avec un config énorme, des content custom partout et des hacks de fonts, tu peux migrer vite… à condition d’accepter le changement de mentalité. Voilà comment je le fais dans Next.js, sans me battre contre la nouvelle logique.
Le vrai changement : Tailwind v4 est “CSS-first” (et ça se sent)
La bascule la plus importante, c’est que Tailwind v4 te pousse à mettre la config “design” directement dans ton CSS, via @theme. Concrètement, tes couleurs, tes fonts, tes rayons, tes breakpoints et tes tokens maison deviennent des variables. Et Tailwind s’appuie dessus pour générer les utilitaires.
Le piège classique, c’est de vouloir recréer ton thème v3 dans un config JS par habitude. Tu peux, mais tu passes à côté du gain. Avec @theme, ton design system est plus lisible, plus proche du rendu final, et surtout plus simple à partager avec le reste de ta stack CSS (CSS Modules, styles “vanilla”, libs externes). Bref, tu reviens à un truc sain : des tokens CSS, puis des utilitaires.
Sponsorisé par Le Scribouillard
Besoin de contenu optimisé SEO ?
Utilisez la meilleure plateforme française de création de contenu assistée par IA ! Et générez des articles pour moins de 1€ !
Installer Tailwind v4 dans Next.js (sans copier-coller un setup v3)
En Next.js, le point clé c’est le pipeline PostCSS et ton CSS global. Tailwind v4 s’importe proprement via @import, et tu n’es plus obligé de dérouler les directives “base/components/utilities” comme avant.
Le setup minimal que j’utilise : un fichier postcss.config.mjs qui active le plugin Tailwind v4, et un app/globals.css (App Router) qui importe Tailwind. Ensuite tu importes ton global CSS dans app/layout.tsx comme d’habitude.
// postcss.config.mjs
export default {
plugins: {
"@tailwindcss/postcss": {},
},
}
// app/layout.tsx (extrait)
import "./globals.css"
import { Inter } from "next/font/google"
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
})
export default function RootLayout({ children }) {
return (
<html lang="fr" className={inter.variable}>
<body>{children}</body>
</html>
)
}Oui, ça fait bizarre au début parce que tu ne “génères” plus Tailwind via un CLI avec des fichiers qui sortent. Mais au quotidien, c’est plus fluide : tu ajustes ton CSS global, et Next rebuild vite.
@theme : où mettre tes tokens (et comment éviter le bazar)
Je te conseille un truc très simple : un seul point d’entrée global pour Tailwind (souvent app/globals.css), et tu déclares ton @theme à un endroit obvious. Pas dans un coin. Pas éparpillé dans 12 fichiers.
Pourquoi ? Parce que sinon tu vas recréer le syndrome “config spaghetti”, mais en CSS. Et c’est pire, parce que tu mélanges vite les overrides, les couches, l’ordre d’import, et tu finis par ne plus savoir quel token gagne.
Dans @theme, je mets les décisions de design qui doivent être stables et réutilisables : palette, arrondis, ombres, espacements si tu en ajoutes, et surtout les fonts. L’idée, c’est que Tailwind devienne une façade sur tes variables plutôt qu’un endroit où tu recopies des valeurs en dur.
/* app/globals.css */
@import "tailwindcss";
/* Si tu es en monorepo ou que tes composants sont hors du dossier Next, tu peux guider le scan.
Exemple : @source "../packages/ui/**/*.{ts,tsx}"; */
@theme {
/* Fonts: on mappe Tailwind sur tes variables (next/font ou autre) */
--font-sans: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
/* Exemple de tokens maison */
--radius-md: 10px;
/* Exemple couleur (garde-le simple, pas une encyclopédie) */
--color-brand: oklch(0.72 0.17 254);
}
@layer base {
html {
font-family: var(--font-sans);
}
}Mon avis (assez sec) : si tu fais un design system, tu vas aimer. Si tu fais un prototype où tout change tous les deux jours, tu peux rester minimal et ne pas sur-tokeniser. Tailwind v4 n’oblige pas à être dogmatique. Il rend juste la voie “propre” plus naturelle.
La détection automatique du contenu : pratique, mais ça peut te surprendre
Autre changement qui fait gagner du temps : Tailwind v4 détecte automatiquement les fichiers à scanner pour trouver tes classes. Donc la gymnastique content: [] de Tailwind v3 devient beaucoup moins centrale. En Next.js classique (app/pages/components/src), ça marche bien et tu n’y penses plus.
Là où ça mord, c’est quand ton code n’est pas “là où Tailwind s’attend à le trouver”. Monorepo, UI kit dans un package à côté, templates générés, fichiers MDX dans un dossier exotique… tout ce qui sort du sentier peut faire “disparaître” des classes, et tu vas croire que Tailwind est cassé. En vrai, il scanne juste pas ce que toi tu considères comme “le projet”. Dans ces cas, tu ajoutes une directive de source côté CSS pour inclure tes dossiers hors scope. Et tu repars.
Et non, ça ne résout pas la règle de base : Tailwind ne lit pas dans tes pensées. Si tu construis des classes dynamiquement genre "bg-" + color, tu joues contre le moteur. Ça a toujours été une mauvaise idée, et v4 ne rend pas ça magique. Utilise des mappings explicites, ou des variants prévisibles.
CSS Modules + Tailwind v4 : cohabitation propre (sans guerre de styles)
Je vois encore des projets qui opposent “Tailwind partout” et “CSS Modules partout” comme si tu devais choisir une religion. En Next.js, les deux cohabitent très bien si tu donnes un rôle clair à chacun.
Tailwind gère très bien la mise en page, les espacements, les états, les layouts responsives et tout ce qui est “utilitaire” et facilement lisible dans le JSX. Les CSS Modules, eux, sont parfaits quand tu as un composant avec une structure un peu dense, un style qui ne se résume pas à 3 classes, ou des règles typographiques spécifiques. Et surtout, ils évitent le composant avec 18 classes Tailwind collées où plus personne n’ose toucher.
Le point d’attention en v4, c’est que tu ne veux pas importer Tailwind dans chaque module (sinon tu te crées des effets de bord et des couches inutiles). Si tu as besoin de trucs Tailwind dans un module (typiquement @apply), Tailwind v4 a prévu un mécanisme de “référence” pour utiliser les features sans ré-émettre toute la base. Garde ça comme un outil ponctuel, pas comme un nouveau standard de codebase.
Fonts : le pattern propre avec next/font + variables CSS
Les fonts, c’est souvent là que les setups Tailwind historiques partent en freestyle. Avec Next.js, next/font te donne un chargement propre, optimisé, avec une variable CSS. Et avec Tailwind v4, tu peux brancher cette variable directement dans @theme, et arrêter de faire des configs “fontFamily” qui se battent avec le reste.
Le pattern que je garde : next/font définit une variable (ex. --font-inter) au niveau du <html>, puis dans @theme tu maps --font-sans vers var(--font-inter). Résultat : Tailwind continue de fonctionner avec font-sans, mais la source de vérité c’est ta variable, pas un tableau de strings dans un config JS.
Bonus : si demain tu passes Inter en local, ou que tu changes de fonte pour une autre, la surface de changement est minuscule. Et tu peux faire cohabiter plusieurs fonts (sans te retrouver avec un Frankenstein de classes).
Donc, on jette tailwind.config.js ? Non. On l’arrête juste quand il ne sert à rien.
Tu peux très bien garder un tailwind.config.js en v4. Mais dans un projet Next.js “classique”, tu vas vite te rendre compte que tu n’en as plus besoin pour 80% des cas. Et c’est une bonne nouvelle, parce que beaucoup de configs v3 étaient juste des caches-misère.
Quand je le garde, c’est pour les sujets qui ne sont pas juste “des tokens” : plugins spécifiques, contraintes d’architecture, besoins avancés de génération, ou compat avec un existant qui ne peut pas bouger tout de suite. Pour le reste, je préfère une base CSS lisible, un @theme clair, et une codebase qui ne dépend pas d’un config file devenu tentaculaire.