ScrollTrigger
ScrollTrigger is a GSAP plugin that ties any animation to scroll position. You give it a trigger element and a start/end range, and it controls when the tween plays, pauses, scrubs with the scrollbar, or pins the element in place. It's the engine behind almost every scroll-driven effect on modern brand sites.
Updated July 8, 2026
How ScrollTrigger works
Every ScrollTrigger has three parts: a TRIGGER element (the scroll-position reference), a START point, and an END point. As the user scrolls, the trigger's position relative to the viewport changes, and ScrollTrigger fires callbacks (or scrubs progress) within the start-to-end range.
The start / end syntax is "<trigger position> <viewport position>". "top 80%" means 'when the TOP of the trigger element crosses the 80% line of the viewport'. "bottom center" means 'when the BOTTOM of the trigger element crosses the viewport's vertical center'.
Free, and worth using responsibly
x, y, scale) rather than layout properties.Three behaviour modes - toggleActions, scrub, pin
ScrollTrigger has three primary behaviour modes that you mix per use case:
1. toggleActions
Fire-and-forget callbacks. The animation plays normally; ScrollTrigger just controls when. Use toggleActions: "play none none reverse" (the most common) to play on enter and reverse on leave-back.
2. scrub
Animation progress is bound to scroll position. The tween's time becomes the scroll position. Use for parallax, scroll-driven reveals, scroll-linked counters.
3. pin
The trigger element sticks in place while scroll progresses. Pin a hero, pin a section, pin a sidebar. Combine with scrub for the classic 'scroll inside this section animates content' pattern.
Use ScrollTrigger for
- Reveal animations as content enters the viewport
- Parallax effects
- Scroll-pinned sections with internal animations
- Progress indicators tied to scroll
- Anything where 'what's visible' needs to drive 'what's animating'
Use something else when
- Pure entrance animation on load - use a normal tween, no ScrollTrigger needed
- Element visibility detection without animation - use
IntersectionObserverdirectly (smaller, no GSAP dep) - Scroll-snap behaviour - native CSS
scroll-snapis better than ScrollTrigger - Smooth scrolling - that's Lenis or
ScrollSmoother, not ScrollTrigger
Used in these Annnimate components
- The Image Dissolve Scroll component scrubs a shader uniform 0→1 across the section's scroll range
- The Mesh Gradient shader uses ScrollTrigger to update color uniforms on scroll
See it running in production
Common questions
- Do I need to register ScrollTrigger before using it?
- Yes. Add
import { ScrollTrigger } from 'gsap/ScrollTrigger'andgsap.registerPlugin(ScrollTrigger)once at module load. In SSR contexts (Next.js), wrap the registration inif (typeof window !== 'undefined')so it only runs client-side. Forgetting to register is the #1 'why isn't my animation firing' cause. - What's the difference between scrub: true and scrub: 1?
scrub: trueties animation progress directly to scroll progress - instant, 1:1.scrub: 1adds a 1-second smoothing lag (the animation 'catches up' over 1 second), which feels more cinematic and forgiving on touchpad jitter. Most production sites use scrub values between 0.5 and 1.5.- Why does my pinned section break on resize?
- ScrollTrigger caches positions when triggers are created. If the layout changes (window resize, dynamic content), call
ScrollTrigger.refresh()to recalculate. Or setinvalidateOnRefresh: trueon individual triggers to also re-read the tween'sfromvalues. Pair with a debounced resize listener. - How do I make a ScrollTrigger fire once on enter and never again?
- Pass
once: trueinstead of (or alongside)toggleActions. This is the right pattern for entrance reveals where you don't want the animation to reverse when the user scrolls back up - common for hero text, image reveals, content tiles. - Can I use ScrollTrigger inside a Next.js Server Component?
- No - ScrollTrigger needs the DOM and window. Wrap your animated component in 'use client' and run the GSAP code inside useGSAP. The page itself can stay a Server Component; only the animated leaf needs to be client.
