Pourquoi tes animations React rament à 30 fps — et le réflexe Framer Motion qui corrige ça
Une animation qui saccade vient presque toujours d'une propriété mal choisie. Le réflexe qui corrige ça, et les patterns Motion (ex-Framer Motion v12) pour des interfaces fluides à 60 fps.
Pourquoi ça rame à 30 fps
Quand une animation React saccade, la cause est presque toujours la même, et elle n'a rien à voir avec la puissance de la machine : on anime la mauvaise propriété. Animer width, height, top, left, margin ou padding force le navigateur à recalculer la mise en page — un reflow — à chaque frame, et c'est exactement ce qui fait tomber une animation de 60 à 30 images par seconde. Le réflexe qui corrige ça tient en une phrase : n'anime que transform et opacity. Ce sont les deux seules propriétés composées par le GPU, qui ne déclenchent aucun reflow.
C'est précisément la valeur par défaut de Motion — le nouveau nom de Framer Motion, disponible sur motion.dev, désormais en version 12 et importé depuis motion/react. La bibliothèque anime transform et opacity par défaut, ce qui te place du bon côté de la performance sans y penser. Mais elle offre aussi des fonctionnalités — les layout animations notamment — qui peuvent te ramener dans le piège du reflow si tu ne sais pas comment elles fonctionnent. Voyons les patterns qui produisent des interfaces vivantes sans sacrifier les 60 fps.
Les fondamentaux : motion components, variants, AnimatePresence
Le point d'entrée est le motion component : motion.div, motion.button transforment un élément en élément animable. Les props initial, animate et exit définissent l'état de départ, l'état cible et l'état de sortie. C'est une API déclarative — on décrit des états, pas des séquences — naturelle pour un designer qui pense en termes visuels.
import { motion, AnimatePresence } from "motion/react";
function Toast({ visible, message }) {
return (
<AnimatePresence>
{visible && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
>
{message}
</motion.div>
)}
</AnimatePresence>
);
}Les variants rendent les animations complexes gérables : on nomme des états — hidden, visible, hover — dans un objet séparé, et ils se propagent aux enfants, avec staggerChildren pour échelonner une cascade sans logique de timing manuelle. Je centralise les variants du projet dans un fichier dédié, ce qui crée un vocabulaire de mouvement cohérent. Et AnimatePresence règle le angle mort de React : sans lui, un composant retiré du DOM disparaît d'un coup ; avec lui, l'animation exit joue avant le retrait — indispensable pour modales, notifications et transitions.
Layout animations et shared element transitions
C'est la fonctionnalité la plus spectaculaire — et la plus piégeuse côté perf. En ajoutant la prop layout, Motion anime automatiquement tout changement de position ou de taille via la technique FLIP. Un filtre qui réordonne une grille, un panneau qui repousse le contenu : tout devient fluide. La prop layoutId va plus loin avec les shared element transitions : même layoutId sur une miniature et sur l'image de détail, et Motion anime la transformation de l'une à l'autre — ce genre de transition d'app native qui était un cauchemar à coder sur le web. LayoutGroup coordonne les recalculs entre animations simultanées pour éviter les conflits.
Le piège : contrairement à transform/opacity, une layout animation lit le DOM pour calculer les changements de position. Avec beaucoup d'éléments animés en même temps, ça peut faire saccader. J'utilise donc layout avec parcimonie et je regroupe les éléments concernés dans un LayoutGroup. C'est exactement le genre de fonctionnalité magnifique en démo qui rame en production si on l'applique partout.
Spring physics, gestes et scroll
Motion utilise par défaut des ressorts physiques plutôt que des courbes de Bézier. Les paramètres stiffness, damping et mass simulent un comportement que le cerveau perçoit comme naturel — masse faible et amortissement élevé pour une micro-interaction nette, masse élevée et amortissement faible pour un rebond ludique. Ces paramètres physiques sont plus intuitifs que des durées en millisecondes. Le système de gestes — whileHover, whileTap, whileDrag, whileInView — crée des interactions tactiles en déclaratif : un carrousel draggable avec inertie tient en quelques lignes. Le hook useScroll expose scrollYProgress pour les barres de progression, parallaxes et révélations. Et pour le SVG, pathLength permet l'animation de tracé progressif, parfaite pour les icônes animées et les loaders.
CSS natif, Motion ou GSAP : quand utiliser quoi
Tout n'a pas besoin de Motion. Voici comment je tranche.
| Besoin | Outil | Pourquoi |
|---|---|---|
| Hover, transitions simples, entrées | CSS natif (@starting-style) | meilleure perf, zéro JS |
| Transition de page (même app) | View Transitions API | natif, Baseline depuis oct. 2025 |
| Layout, gestes, orchestration | Motion | ratio effort/résultat imbattable |
| Timeline cinématographique précise | GSAP | contrôle maximal |
La View Transitions API mérite une mention : les transitions same-document sont passées Baseline en octobre 2025, et les transitions cross-document (multi-pages) fonctionnent dans Chrome et Edge 126+ et Safari 18.2+, Firefox étant encore en cours — donc sur ces moteurs, la transition est simplement ignorée sans casse. Pour une navigation simple, cette API native peut suffire et t'évite d'embarquer une bibliothèque. Dans la pratique, je combine souvent les trois approches dans un même projet, chacune là où elle excelle.
Performance et accessibilité en production
Tu l'as compris : la règle de perf tient à animer transform et opacity, et à traiter les layout animations comme un outil de précision, pas un réflexe — layout avec parcimonie, regroupé dans un LayoutGroup. L'autre exigence non négociable est l'accessibilité. Le hook useReducedMotion lit la préférence système prefers-reduced-motion ; je l'intègre systématiquement pour désactiver ou simplifier les animations chez les personnes sensibles au mouvement. Un bouton qui revient à une transition instantanée reste parfaitement fonctionnel.
Ma règle d'or reste fonctionnelle : si un mouvement ne rend pas l'interface plus compréhensible ou plus agréable, il n'a pas sa place. Chaque animation doit guider l'attention, signaler un changement d'état, créer une continuité spatiale ou donner un feedback. Une animation qui n'existe que pour être remarquée est une animation de trop. À 60 fps comme à 30, la retenue est le vrai signe de maîtrise.
15 micro-interactions CSS sans une ligne de JavaScript (ce qui exigeait une lib hier)
Le soulignement qui glisse, la carte qui bascule en 3D, l'entrée animée sans display: none bricolé : 15 micro-interactions en CSS pur, à 60 fps, sans une ligne de JavaScript.
Les 4 patterns React qui survivent à 18 mois de croissance produit (et les 3 qui s'effondrent)
Un projet React propre peut devenir un enchevêtrement en quelques mois. Voici les 4 patterns de composition qui tiennent à la croissance — et les 3 qui s'effondrent à coup sûr, avec React 19.
Du token Figma au composant React en 5 étapes — et la moitié de la glue d'avant ne sert plus à rien
Le pipeline token→composant que tout le monde recopie est à moitié périmé. Depuis Tailwind v4 et la spec Design Tokens stable, voici les 5 étapes qui comptent vraiment pour passer de Figma à React.