Contents
Contents
GSAP (GreenSock Animation Platform) is the industry standard for high-performance web animations. It works with plain JavaScript, React, Vue, and anything else — and it runs consistently across every browser.
The fundamental building block in GSAP is the tween — a single animation that moves a property from one value to another over a set duration.
gsap.to(".box", { x: 100, duration: 1 });gsap.to() animates to the target values. There's also gsap.from() (animate from) and gsap.fromTo() (full control over start and end).
When you need multiple animations to run in sequence, use a Timeline:
const tl = gsap.timeline();
tl.to(".box", { x: 100, duration: 0.5 })
.to(".box", { y: 50, duration: 0.5 })
.to(".box", { rotation: 360, duration: 0.8 });Timelines let you offset animations, reverse them, pause them, and control playback speed — all with a single object.
Easing is what makes animations feel physical rather than mechanical. GSAP ships with a full easing library:
gsap.to(".box", {
x: 200,
duration: 1,
ease: "power2.inOut",
});Some useful defaults to know: power2.out for exits, back.out(1.7) for bouncy arrivals, expo.inOut for dramatic transitions.
ScrollTrigger is a GSAP plugin that ties animations to scroll position. It's what powers most modern scroll-driven experiences.
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
gsap.to(".section", {
opacity: 1,
y: 0,
scrollTrigger: {
trigger: ".section",
start: "top 80%",
end: "top 40%",
scrub: true,
},
});The scrub option ties the animation progress directly to scroll position rather than playing it once on enter.
If you're using Lenis for smooth scrolling (like this portfolio does), you need to sync ScrollTrigger with Lenis' virtual scroll position. Otherwise, trigger points will misfire.
lenis.on("scroll", ScrollTrigger.update);
gsap.ticker.add((time) => {
lenis.raf(time * 1000);
});
gsap.ticker.lagSmoothing(0);This wires Lenis' scroll events into ScrollTrigger so they stay in sync.
Always kill tweens and ScrollTriggers when components unmount — otherwise you'll accumulate leaked animations.
useEffect(() => {
const ctx = gsap.context(() => {
gsap.to(".box", { x: 100 });
}, containerRef);
return () => ctx.revert();
}, []);gsap.context() captures all animations created inside it and ctx.revert() undoes everything on cleanup — the cleanest pattern for React.