iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
Formations
JavaScript · Intermédiaire

GSAP

Maîtriser GSAP de zéro : tweens, timelines, ScrollTrigger, plugins et intégration React/Next.js pour créer des animations web professionnelles.

GSAPJavaScriptReactNext.jsScrollTrigger
01Introduction à GSAP02Tweens : to, from, fromTo03Timelines04ScrollTrigger05Stagger, utils et matchMedia06GSAP dans React et Next.js07Plugins et techniques avancées
Chapitre 6·25 min

GSAP dans React et Next.js

GSAP fonctionne très bien avec React, mais il faut comprendre quelques règles. React gère le DOM différemment d'une page HTML statique — les composants montent, se démontent, et re-rendent. Sans cleanup correct, on accumule des animations orphelines qui causent des bugs impossibles à déboguer.

Le package @gsap/react

GreenSock fournit un package React officiel avec un hook useGSAP qui gère automatiquement le scope et le cleanup.

npm install gsap @gsap/react

useGSAP — le hook fondamental

'use client'
 
import { useRef } from 'react'
import gsap from 'gsap'
import { useGSAP } from '@gsap/react'
 
gsap.registerPlugin(useGSAP)
 
export default function Hero() {
  const containerRef = useRef<HTMLDivElement>(null)
 
  useGSAP(() => {
    gsap.from('.hero-title', {
      y: 40,
      opacity: 0,
      duration: 0.8,
      ease: 'power3.out',
    })
 
    gsap.from('.hero-sub', {
      y: 20,
      opacity: 0,
      duration: 0.6,
      delay: 0.2,
    })
  }, { scope: containerRef })  // ← scope au conteneur
 
  return (
    <div ref={containerRef}>
      <h1 className="hero-title">Titre</h1>
      <p className="hero-sub">Sous-titre</p>
    </div>
  )
}

{ scope: containerRef } fait que les sélecteurs CSS dans le hook cherchent uniquement dans containerRef. Sans ça, .hero-title trouverait tous les éléments avec cette classe dans la page entière.

Le cleanup est automatique : quand le composant se démonte, useGSAP annule toutes les animations créées dans son callback et remet les éléments à leur état d'origine.

Registering plugins

Enregistrer les plugins une seule fois, en dehors des composants.

// lib/gsap.ts — fichier dédié
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import { useGSAP } from '@gsap/react'
 
gsap.registerPlugin(ScrollTrigger, useGSAP)
 
export { gsap, ScrollTrigger, useGSAP }
// Dans les composants
import { gsap, ScrollTrigger, useGSAP } from '@/lib/gsap'

Ne jamais appeler registerPlugin dans un composant React — il est appelé à chaque render, même si GSAP déduplique en interne.

Animer avec des refs

Pour animer un élément précis (sans sélecteur CSS), utiliser useRef.

'use client'
 
import { useRef } from 'react'
import gsap from 'gsap'
import { useGSAP } from '@gsap/react'
 
export default function AnimatedCard() {
  const cardRef = useRef<HTMLDivElement>(null)
  const titleRef = useRef<HTMLHeadingElement>(null)
 
  useGSAP(() => {
    gsap.from(cardRef.current, {
      y: 60,
      opacity: 0,
      duration: 0.7,
    })
 
    gsap.from(titleRef.current, {
      x: -20,
      opacity: 0,
      duration: 0.5,
      delay: 0.2,
    })
  })
 
  return (
    <div ref={cardRef} className="card">
      <h2 ref={titleRef}>Titre</h2>
    </div>
  )
}

contextSafe — animations déclenchées par des événements

useGSAP retourne une fonction contextSafe pour les animations déclenchées par des événements (clics, hover) — pas dans le setup initial.

'use client'
 
import { useRef } from 'react'
import gsap from 'gsap'
import { useGSAP } from '@gsap/react'
 
export default function Button() {
  const btnRef = useRef<HTMLButtonElement>(null)
 
  const { contextSafe } = useGSAP()
 
  const handleClick = contextSafe(() => {
    gsap.to(btnRef.current, {
      scale: 0.95,
      duration: 0.1,
      yoyo: true,
      repeat: 1,
    })
  })
 
  return (
    <button ref={btnRef} onClick={handleClick}>
      Cliquer
    </button>
  )
}

Sans contextSafe, les animations créées dans les event handlers ne sont pas associées au contexte GSAP du composant — elles ne seront pas nettoyées quand le composant se démonte.

ScrollTrigger dans React

'use client'
 
import { useRef } from 'react'
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import { useGSAP } from '@gsap/react'
 
gsap.registerPlugin(ScrollTrigger)
 
export default function Section() {
  const sectionRef = useRef<HTMLElement>(null)
 
  useGSAP(() => {
    const tl = gsap.timeline({
      scrollTrigger: {
        trigger: sectionRef.current,
        start: 'top 75%',
        toggleActions: 'play none none reverse',
      },
    })
 
    tl.from('.section-title', { y: 30, opacity: 0, duration: 0.6 })
      .from('.section-body', { y: 20, opacity: 0, duration: 0.5 }, '-=0.2')
  }, { scope: sectionRef })
 
  return (
    <section ref={sectionRef}>
      <h2 className="section-title">Titre</h2>
      <p className="section-body">Contenu</p>
    </section>
  )
}

useGSAP s'occupe du cleanup des ScrollTriggers aussi — ils sont détruits quand le composant se démonte.

Next.js et le SSR

GSAP est une bibliothèque client — elle a besoin du DOM. Next.js rend côté serveur, où document et window n'existent pas.

Règle n°1 : tous les composants GSAP sont 'use client'.

'use client'  // ← obligatoire
 
import { useGSAP } from '@gsap/react'

Règle n°2 : ne jamais appeler GSAP au niveau module dans un composant server.

// ❌ INTERDIT — exécuté côté serveur
import gsap from 'gsap'
gsap.to('.box', { opacity: 0 })  // crash : document is not defined
 
// ✅ CORRECT — dans un composant client
'use client'
import { useGSAP } from '@gsap/react'
useGSAP(() => {
  gsap.to('.box', { opacity: 0 })  // exécuté seulement dans le navigateur
})

Règle n°3 : importer les plugins dynamiquement si nécessaire.

Dans les cas rares où vous importez GSAP dans un contexte mixte client/serveur :

import dynamic from 'next/dynamic'
 
const AnimatedHero = dynamic(() => import('./AnimatedHero'), {
  ssr: false,  // ne rend pas côté serveur
})

Pattern complet : composant avec ScrollTrigger et responsive

'use client'
 
import { useRef } from 'react'
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import { useGSAP } from '@gsap/react'
 
gsap.registerPlugin(ScrollTrigger)
 
export default function Features() {
  const containerRef = useRef<HTMLDivElement>(null)
 
  useGSAP(() => {
    const mm = gsap.matchMedia()
 
    mm.add('(prefers-reduced-motion: no-preference)', () => {
      ScrollTrigger.batch('.feature-card', {
        onEnter: (elements) => {
          gsap.from(elements, {
            y: 50,
            opacity: 0,
            duration: 0.7,
            stagger: 0.12,
            ease: 'power3.out',
          })
        },
        start: 'top 85%',
      })
    })
  }, { scope: containerRef })
 
  return (
    <div ref={containerRef} className="grid grid-cols-1 md:grid-cols-3 gap-6">
      <div className="feature-card">...</div>
      <div className="feature-card">...</div>
      <div className="feature-card">...</div>
    </div>
  )
}

Erreurs courantes à éviter

Sélecteur global sans scope :

// ❌ Sélectionne .title dans toute la page
useGSAP(() => {
  gsap.from('.title', { opacity: 0 })
})
 
// ✅ Scoped au composant
useGSAP(() => {
  gsap.from('.title', { opacity: 0 })
}, { scope: containerRef })

Animer dans useEffect au lieu de useGSAP :

// ❌ Pas de cleanup automatique, pas de scope
useEffect(() => {
  gsap.from('.box', { opacity: 0 })
}, [])
 
// ✅ useGSAP gère le cleanup
useGSAP(() => {
  gsap.from('.box', { opacity: 0 })
}, { scope: ref })

Ne pas enregistrer les plugins :

// ❌ ScrollTrigger non enregistré
import { ScrollTrigger } from 'gsap/ScrollTrigger'
// ... utiliser scrollTrigger: {} → erreur silencieuse
 
// ✅
gsap.registerPlugin(ScrollTrigger)

GSAP dans React est propre dès qu'on utilise useGSAP correctement. Le dernier chapitre couvre les plugins avancés : TextPlugin, Flip, MotionPathPlugin, et un aperçu des plugins Club GSAP.

Précédent
Stagger, utils et matchMedia
Suivant
Plugins et techniques avancées

Développeur fullstack passionné. J'apprends en construisant et je documente tout — front, back, outils. Le code s'apprend mieux en public.

Naviguer

IndexTous les articlesFormationsProfilOutilsBibliothech

Ailleurs

GitHub RSS

Newsletter

Les articles, libs et découvertes. Une fois par semaine, pas plus.

© 2026 William LoreeConçu & codé à la main