Building Awwwards-Level Animations with GSAP and Next.js
When building portfolio sites or creative web experiences, animation quality is what separates good from exceptional. In this post, I'll share the patterns I use to build scroll-driven animations that feel truly premium.
Setting Up GSAP with Next.js App Router
The key to GSAP in React is the useGSAP hook from @gsap/react. It handles cleanup automatically, preventing memory leaks when components unmount.
import { useGSAP } from "@gsap/react";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
export function AnimatedSection() {
const containerRef = useRef<HTMLDivElement>(null);
useGSAP(() => {
gsap.from(".fade-item", {
opacity: 0,
y: 80,
stagger: 0.08,
ease: "expo.out",
duration: 1.2,
scrollTrigger: {
trigger: containerRef.current,
start: "top 85%",
},
});
}, { scope: containerRef });
return <div ref={containerRef}>...</div>;
}
The Hydration Problem
GSAP and server-side rendering don't always play nicely. The solution is to set initial animation states in CSS and animate TO the final state in GSAP — never use gsap.from() with values that conflict with SSR-rendered markup.
This is the single most impactful technique for eliminating the "flash of unstyled content" that plagues GSAP animations in Next.js apps.