CSS natif en 2026 : pourquoi Tailwind n'est plus la seule option
Container queries, :has(), nesting natif, view transitions — le CSS de 2026 fait ce que JavaScript et Sass faisaient avant. Tour d'horizon des fonctionnalités qui changent la donne.

Depuis Next.js 13, chaque composant React est un Server Component par défaut. Ajoutez "use client" en haut du fichier, et il devient un Client Component. Simple en apparence, mais la distinction a des implications énormes sur les performances de votre site.
Après avoir migré plusieurs projets vers l'App Router, voici ce que j'ai appris sur quand utiliser chacun.
Un Server Component s'exécute uniquement sur le serveur. Il génère du HTML qui est envoyé au navigateur. Aucun JavaScript n'est envoyé pour ce composant.
Un Client Component s'exécute d'abord sur le serveur (pour le rendu initial), puis est "hydraté" côté client. Le JavaScript du composant est envoyé au navigateur pour permettre l'interactivité.
Concrètement, si vous avez un composant qui affiche une liste de produits sans aucune interactivité, le laisser en Server Component signifie zéro JavaScript envoyé. Le navigateur reçoit juste du HTML statique.
La règle est simple : ajoutez "use client" uniquement quand vous en avez besoin. Et vous en avez besoin dans trois cas :
1. Événements utilisateur
"use client";
export function AddToCartButton() {
const handleClick = () => {
// Logique d'ajout au panier
};
return <button onClick={handleClick}>Ajouter</button>;
}
Tout ce qui utilise onClick, onChange, onSubmit nécessite du JavaScript côté client.
2. Hooks React
"use client";
import { useState, useEffect } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
useState, useEffect, useContext, useReducer — tous ces hooks nécessitent "use client".
3. APIs navigateur
"use client";
export function CurrentUrl() {
const url = window.location.href;
return <p>Vous êtes sur : {url}</p>;
}
window, document, localStorage, navigator n'existent que côté client.
L'erreur classique : mettre "use client" sur un composant parent parce qu'un petit bouton enfant en a besoin.
// ❌ MAUVAIS : tout le composant devient client
"use client";
export function ProductPage({ product }) {
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<img src={product.image} />
<AddToCartButton productId={product.id} />
</div>
);
}
Le titre, la description, l'image — tout ça n'a pas besoin de JavaScript. Mais en mettant "use client" sur le parent, vous envoyez du JS inutile.
// ✅ BON : seul le bouton est client
// app/product/page.tsx (Server Component par défaut)
import { AddToCartButton } from "./AddToCartButton";
export function ProductPage({ product }) {
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<img src={product.image} />
<AddToCartButton productId={product.id} />
</div>
);
}
// app/product/AddToCartButton.tsx
"use client";
export function AddToCartButton({ productId }) {
const handleClick = () => addToCart(productId);
return <button onClick={handleClick}>Ajouter</button>;
}
Le composant parent reste un Server Component. Seul le bouton est hydraté.
Sur un site e-commerce que j'ai optimisé, passer de "tout en client" à cette approche granulaire a réduit le bundle JavaScript de 340 KB à 89 KB.
Le Time to Interactive (TTI) est passé de 4,2 secondes à 1,8 seconde sur mobile 3G.
Les Core Web Vitals se sont améliorés : le INP (Interaction to Next Paint) est passé de "needs improvement" à "good".
Piège 1 : Passer des fonctions aux Server Components
// ❌ Erreur : on ne peut pas passer une fonction d'un Server à un Client Component
export function ProductPage() {
const handleClick = () => console.log("click");
return <ClientButton onClick={handleClick} />;
}
Les fonctions ne sont pas sérialisables. Définissez-les dans le Client Component lui-même.
Piège 2 : Importer un module client dans un Server Component
Si un fichier utilise "use client", tous les modules qu'il importe deviennent aussi client. Faites attention à vos barrel exports (index.ts).
Piège 3 : Oublier que les enfants restent server
"use client";
export function Modal({ children }) {
const [open, setOpen] = useState(false);
return open ? <div className="modal">{children}</div> : null;
}
Même si Modal est client, les children passés depuis un Server Component restent des Server Components. Ils sont pré-rendus sur le serveur et passés comme props.
Je commence toujours sans "use client". Quand j'ai une erreur du type "useState is not defined" ou "window is not defined", je crée un composant enfant minimal avec "use client" juste pour la partie interactive.
Cette approche "bottom-up" garantit que le minimum de JavaScript est envoyé au navigateur.
Server Components ne sont pas juste une optimisation. C'est un changement de paradigme. Au lieu de penser "tout est JavaScript", on pense "le HTML d'abord, le JavaScript seulement quand nécessaire".
Pour un site vitrine, vous pouvez atteindre zéro JavaScript côté client. Pour une application interactive, vous minimisez le bundle en isolant l'interactivité.
C'est comme ça que Next.js permet d'avoir le meilleur des deux mondes : la performance du HTML statique avec l'interactivité de React quand on en a besoin.
Besoin d'aide ?
Container queries, :has(), nesting natif, view transitions — le CSS de 2026 fait ce que JavaScript et Sass faisaient avant. Tour d'horizon des fonctionnalités qui changent la donne.
Les sites statiques appartiennent au passé. Découvrez comment les micro-interactions fluides avec Framer Motion augmentent l'engagement utilisateur et le taux de conversion.
Un site WordPress qui mettait 12 secondes à charger est passé à 1,8 seconde en corrigeant trois erreurs courantes. Voici lesquelles.