Introduction
Framer Motion est la bibliothèque d'animations la plus puissante pour React en 2026, surpassant les APIs CSS natives grâce à sa déclaration simple et ses performances GPU-accélérées. Pour les développeurs avancés, elle excelle dans les cas complexes : animations de layout responsives, gestes tactiles fluides, transitions scroll-linked et orchestrations d'équipes d'éléments.
Pourquoi l'adopter ? Les animations ne sont plus des gadgets : elles guident l'utilisateur, améliorent l'accessibilité (via useReducedMotion) et boostent les métriques Core Web Vitals comme CLS (Cumulative Layout Shift). Ce tutoriel avancé vous guide pas à pas, du setup à des implémentations production-ready, avec du code TypeScript complet et fonctionnel. À la fin, vous créerez des UIs qui rivalisent avec celles de Figma ou Notion. Prêt à transformer vos composants statiques en expériences immersives ? (142 mots)
Prérequis
- Node.js 20+ et npm/yarn/pnpm
- React 18+ avec Vite ou Next.js
- Connaissances avancées en TypeScript et hooks React
- Un éditeur comme VS Code avec extension Framer Motion (optionnel)
- CodeSandbox ou StackBlitz pour tester instantanément
Setup du projet et installation
npm create vite@latest framer-motion-advanced -- --template react-ts
cd framer-motion-advanced
npm install
npm install framer-motion@latest
npm run devCe script initialise un projet Vite React + TypeScript, installe Framer Motion (version 11+ en 2026) et lance le serveur dev. Vite assure un HMR ultra-rapide pour itérer sur les animations sans rebuild complet. Évitez Create React App : trop lent pour les previews animées.
Les motion components avancés
Les motion.* remplacent les div, span etc., en ajoutant des props comme animate, whileHover. Pour l'avancé, focus sur layout pour animer les changements de taille/position sans JavaScript custom, évitant les layout thrashing.
Animations de layout basiques
import { useState } from 'react';
import { motion } from 'framer-motion';
function App() {
const [isOpen, setIsOpen] = useState(false);
return (
<div style={{ padding: '2rem' }}>
<motion.button
onClick={() => setIsOpen(!isOpen)}
style={{ marginBottom: '1rem' }}
>
Toggle
</motion.button>
<motion.div
layout
style={{
width: isOpen ? 400 : 200,
height: 200,
backgroundColor: '#ff6b6b',
borderRadius: 8
}}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
/>
</div>
);
}
export default App;
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);Ce composant démontre layout : l'élément s'étend fluidement sans saut. initial et animate gèrent l'entrée. Piège : sans layout, les changements DOM cassent l'animation – toujours l'ajouter pour responsive. Copiez-collez dans Vite pour tester.
Variants et transitions orchestrées
Les variants centralisent les états (open/closed), comme un chef d'orchestre. transition affine easing, delay et stagger pour des effets naturels. Analogy : un menu hamburger où les lignes stagger vers le X.
Menu avec variants et stagger
import { useState } from 'react';
import { motion } from 'framer-motion';
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
};
const itemVariants = {
hidden: { y: -20, opacity: 0 },
visible: {
y: 0,
opacity: 1,
transition: { duration: 0.3, ease: 'easeOut' }
}
};
function App() {
const [isOpen, setIsOpen] = useState(false);
return (
<div style={{ padding: '2rem' }}>
<motion.button
onClick={() => setIsOpen(!isOpen)}
style={{ marginBottom: '1rem' }}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
Menu
</motion.button>
<motion.ul
layout
variants={containerVariants}
initial="hidden"
animate={isOpen ? 'visible' : 'hidden'}
style={{ listStyle: 'none', padding: 0 }}
>
<motion.li variants={itemVariants}>Item 1</motion.li>
<motion.li variants={itemVariants}>Item 2</motion.li>
<motion.li variants={itemVariants}>Item 3</motion.li>
</motion.ul>
</div>
);
}
export default App;
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);staggerChildren orchestre les enfants comme un ballet. whileHover/tap ajoute du feedback tactile. Piège : oublier custom pour passer des props aux enfants – ici implicite via variants. Parfait pour modals ou accordéons.
Gestures avancées avec drag
import { motion } from 'framer-motion';
function App() {
return (
<div style={{ padding: '2rem', height: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<motion.div
drag
dragConstraints={{ top: -50, left: -50, right: 50, bottom: 50 }}
dragElastic={0.2}
whileDrag={{ scale: 1.1, rotate: 5 }}
style={{
width: 100,
height: 100,
backgroundColor: '#4ecdc4',
borderRadius: 12,
cursor: 'grab'
}}
/>
</div>
);
}
export default App;
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);drag active le drag natif multiplateforme (touch/mouse). dragConstraints limite à un viewport, dragElastic ajoute rebond. whileDrag anime pendant l'interaction. Piège : sans cursor: grab, UX confuse – toujours ajouter pour mobile-first.
Animations liées au scroll
Avec useScroll et useTransform, liez des propriétés CSS au scroll comme Parallax pro. Idéal pour hero sections ou timelines : l'opacité fade-in au scroll, sans IntersectionObserver verbeux.
Parallax scroll-triggered
import { useScroll, useTransform } from 'framer-motion';
import { useRef } from 'react';
import { motion } from 'framer-motion';
function App() {
const ref = useRef(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ['start end', 'end start']
});
const y = useTransform(scrollYProgress, [0, 1], [0, -200]);
return (
<div style={{ height: '200vh' }}>
<section style={{ height: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<h1 style={{ fontSize: '3rem' }}>Scroll down!</h1>
</section>
<motion.section
ref={ref}
style={{
height: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
y
}}
>
<motion.div
style={{
width: 300,
height: 300,
backgroundColor: '#45b7d1',
borderRadius: 20
}}
animate={{ opacity: scrollYProgress }}
/>
</motion.section>
</div>
);
}
export default App;
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);useScroll tracke le progrès, useTransform mappe [0,1] vers des valeurs (ex: y parallax). offset définit quand démarrer. Piège : oublier ref sur target – animation ne trigger pas. Optimisé pour 60fps même sur mobile.
AnimatePresence avec exits
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
function App() {
const [items, setItems] = useState(['1', '2', '3']);
const handleAdd = () => setItems((prev) => [...prev, `${prev.length + 1}`]);
const handleRemove = (index: number) => setItems((prev) => prev.filter((_, i) => i !== index));
return (
<div style={{ padding: '2rem' }}>
<button onClick={handleAdd}>Add</button>
<AnimatePresence>
{items.map((item, index) => (
<motion.div
key={item}
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -50, scale: 0.8 }}
transition={{ duration: 0.3 }}
style={{
padding: '1rem',
margin: '0.5rem 0',
backgroundColor: '#f0932b',
borderRadius: 8
}}
onClick={() => handleRemove(index)}
>
Item {item}
</motion.div>
))}
</AnimatePresence>
</div>
);
}
export default App;
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);AnimatePresence anime les exits lors du unmount, essentiel pour listes dynamiques. key unique trigger l'animation. Piège : sans AnimatePresence, exits instantanés – casse l'UX. Utilisez pour toasts ou carrousels.
Bonnes pratiques
- Toujours utiliser
layoutIdpour shared layouts entre routes (Next.js). - Respectez
prefers-reduced-motionviapour accessibilité. - Bundleisez avec
framer-motion/layoutpour tree-shaking. - Testez sur mobile :
dragPropagation={false}évite conflits. - Profilez avec React DevTools Profiler pour détecter re-renders excessifs.
Erreurs courantes à éviter
- Oublier
layout: provoque CLS pénalisant SEO. animateinline sansvariants: code répétitif et dur à maintenir.- Ignorer
mode="wait"dans AnimatePresence : overlaps chaotiques. useTransformsansoffsetprécis : animations imprévisibles au scroll.
Pour aller plus loin
- Docs officielles : framer.com/motion
- Exemples avancés : CodeSandbox Framer Motion
- Performance : Intégrez avec
@react-three/dreipour 3D. - Découvrez nos formations Learni sur React avancé pour maîtriser Next.js + Framer en prod.