Skip to content
Learni
Voir tous les tutoriels
Développement Frontend

Comment animer votre app React avec Framer Motion en 2026

Read in English

Introduction

En 2026, les animations ne sont plus un luxe mais un standard pour des interfaces engageantes et performantes. Framer Motion, la bibliothèque officielle de Framer pour React, excelle par sa déclaration simple, ses APIs puissantes et son accélération GPU native, surpassant CSS transitions ou GSAP en intégration React. Contrairement aux libs génériques, elle gère nativement les gestures, le scroll et les layout shifts sans boilerplate.

Ce tutoriel intermediate vous guide pour animer une app Next.js : du basique au scroll-linked, avec 6 étapes codées. Vous apprendrez à créer des micro-interactions fluides qui boostent la rétention utilisateur de 20-30% (selon études Nielsen). À la fin, votre app rivalisera avec les benchmarks Figma/Webflow. Prêt à transformer vos composants statiques en expériences vivantes ? (128 mots)

Prérequis

  • Node.js 20+ installé
  • Bases solides en React 18+ et TypeScript
  • Familiarité avec Next.js 14 (App Router)
  • Éditeur VS Code avec extension Tailwind CSS IntelliSense
  • Connaissances CSS/Flexbox pour le positionnement

Initialiser le projet Next.js

terminal
npx create-next-app@latest framer-motion-demo --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
cd framer-motion-demo
npm install framer-motion
npm run dev

Cette commande crée un projet Next.js 14 prêt pour TypeScript et Tailwind, puis installe Framer Motion (v11+ en 2026). Elle configure l'App Router pour les pages server-side et les aliases d'imports. Lancez npm run dev pour un serveur hot-reload sur http://localhost:3000. Évitez les templates legacy pour profiter des optimisations RSC.

Premier rendu animé

Remplacez le contenu par défaut de src/app/page.tsx pour tester Framer Motion. Nous utilisons motion.div comme wrapper universel : il remplace div et ajoute des props d'animation comme initial, animate et transition. Analogy : comme un div supercharged avec physique réelle (springs, easing).

Composant animé basique

src/app/page.tsx
import { motion } from 'framer-motion';

export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-center p-24 bg-gradient-to-br from-blue-400 to-purple-600">
      <motion.div
        initial={{ opacity: 0, y: 50 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.8, ease: 'easeOut' }}
        className="text-4xl font-bold text-white mb-8"
      >
        Bienvenue à Framer Motion !
      </motion.div>
      <motion.button
        whileHover={{ scale: 1.05 }}
        whileTap={{ scale: 0.95 }}
        className="px-8 py-4 bg-white text-blue-600 rounded-lg font-semibold shadow-lg"
      >
        Cliquez-moi
      </motion.button>
    </main>
  );
}

Ce code remplace la page d'accueil par une entrée fade-in/slide-up et un bouton avec hover/tap scale. initial définit l'état de départ, animate l'état cible ; whileHover cible les interactions. Copiez-collez directement : il est autonome et optimise les re-renders via useReducedMotion. Piège : oubliez transition pour des defaults brusques.

Maîtriser les variants

Variants centralisent les états multiples en objet : idéal pour orchestrer des groupes d'éléments (orchestration). Analogy : un chef d'orchestre pour vos animations, évitant la duplication de props. Utilisez animate={state} pour switcher dynamiquement.

Animation avec variants

src/components/AnimatedList.tsx
import { motion, useMotionValue, useSpring } from 'framer-motion';

import { useState } from 'react';

type ItemProps = { title: string; index: number };

const Item = ({ title, index }: ItemProps) => (
  <motion.li
    variants={{
      hidden: { opacity: 0, x: -50 },
      visible: (i = 1) => ({
        opacity: 1,
        x: 0,
        transition: { delay: i * 0.1 }
      })
    }}
    initial="hidden"
    animate="visible"
    custom={index}
    className="p-4 bg-white rounded shadow-md mb-2"
  >
    {title}
  </motion.li>
);

export default function AnimatedList() {
  const [items] = useState(['Élément 1', 'Élément 2', 'Élément 3']);

  return (
    <motion.ul
      initial="hidden"
      whileInView="visible"
      variants={{
        hidden: { opacity: 0 },
        visible: { opacity: 1 }
      }}
      className="w-80"
    >
      {items.map((item, index) => (
        <Item key={item} title={item} index={index} />
      ))}
    </motion.ul>
  );
}

Ce composant liste animée utilise variants pour stagger (délai par index via custom). whileInView déclenche au scroll viewport. Importez-le dans page.tsx via . Avantage : enfant hérite des variants parents. Piège : sans custom, pas de stagger ; testez sur mobile pour useReducedMotion.

Personnaliser les transitions

Les transitions contrôlent le 'comment' : duration, ease, type ('spring' pour rebond naturel). En 2026, priorisez springs pour UX premium (plus organique que cubic-bezier).

Transitions avancées avec springs

src/components/SpringCard.tsx
import { motion } from 'framer-motion';

export default function SpringCard() {
  return (
    <motion.div
      initial={{ scale: 0, rotate: -180 }}
      animate={{ scale: 1, rotate: 0 }}
      transition={{
        type: 'spring',
        stiffness: 300,
        damping: 20,
        mass: 1
      }}
      whileHover={{
        scale: 1.1,
        rotate: 5,
        transition: { type: 'spring', stiffness: 400 }
      }}
      className="w-64 h-64 bg-gradient-to-r from-green-400 to-blue-500 rounded-xl shadow-2xl flex items-center justify-center text-white font-bold text-xl cursor-pointer"
    >
      Spring Magic
    </motion.div>
  );
}

Springs simulent la physique réelle : stiffness (raideur), damping (froissement). Hover override la transition globale. Copiez dans page.tsx. Résultat : rebond fluide 60fps. Piège : valeurs extrêmes causent overshoot ; tunez via Framer Motion playground.

Ajouter des gestures interactives

whileDrag, drag activent le drag&drop natif multi-touch. Parfait pour cards, sliders. Intégrez useMotionValue pour sync valeurs custom.

Composant draggable

src/components/DragCard.tsx
import { motion, useMotionValue } from 'framer-motion';

export default function DragCard() {
  const x = useMotionValue(0);
  const y = useMotionValue(0);

  return (
    <motion.div
      drag
      dragConstraints={{ top: -50, left: -50, right: 50, bottom: 50 }}
      dragElastic={0.2}
      style={{ x, y }}
      whileDrag={{ scale: 1.2, rotate: 10 }}
      className="w-48 h-48 bg-purple-500 rounded-lg shadow-lg flex items-center justify-center text-white font-bold cursor-grab active:cursor-grabbing"
    >
      Glissez-moi
    </motion.div>
  );
}

drag active le drag ; constraints limite la zone. useMotionValue tracke position pour sync (ex: parallax). dragElastic ajoute rebond. Testez touch/mobile. Piège : sans style={{x,y}}, pas de sync ; performant même sur low-end.

Animations liées au scroll

useScroll + useTransform lie animations au viewport. Ex: parallax, progress bars. motionValue pour granularité.

Effet parallax scroll

src/components/ScrollParallax.tsx
import { motion, useScroll, useTransform } from 'framer-motion';

import { useRef } from 'react';

export default function ScrollParallax() {
  const ref = useRef(null);
  const { scrollYProgress } = useScroll({
    target: ref,
    offset: ['start end', 'end start']
  });
  const y = useTransform(scrollYProgress, [0, 1], ['0%', '-30%]);

  return (
    <section ref={ref} className="h-screen flex items-center justify-center bg-gradient-to-b from-indigo-500 to-violet-600 relative overflow-hidden">
      <motion.div
        style={{ y }}
        className="text-6xl font-black text-white drop-shadow-2xl"
      >
        Parallax
      </motion.div>
      <motion.div
        style={{ opacity: useTransform(scrollYProgress, [0, 0.5, 1], [0, 1, 0]) }}
        className="absolute bottom-10 left-1/2 transform -translate-x-1/2 text-white text-lg"
      >
        Scroll pour magie
      </motion.div>
    </section>
  );
}

useScroll tracke progress ; useTransform mappe à props (y/opacity). offset définit triggers. Ajoutez à page avec hauteur. Résultat : fond parallax fluide. Piège : target ref requis ; throttle auto pour perf.

Bonnes pratiques

  • Lazy-load : Utilisez Suspense + whileInView pour animations off-screen.
  • Perf : transformTemplate pour GPU ; limitez à 3-5 motions par frame.
  • Accessibilité : Intégrez useReducedMotion via motionValue.
  • Orchestration : layoutId pour shared layout transitions.
  • Tests : Snapshot avec @testing-library/react + mock MotionValues.

Erreurs courantes à éviter

  • Re-renders infinis : Évitez animate dans useEffect sans deps ; utilisez useAnimation.
  • Layout thrashing : Ne mélangez pas position: relative avec scales ; priorisez transform.
  • Mobile lag : Oubliez dragMomentum = false pour stops nets.
  • SSR mismatch : initial={false} sur client-only ; utilisez AnimatePresence pour exits.

Pour aller plus loin

Intégrez avec Remotion pour vidéos ou Framer tool pour prototypes no-code.