Scroll

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

Mechanics

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.

script.js

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

ScrollTrigger and every other GSAP plugin have been free for commercial use since GSAP 3.13 (2024), when Webflow began sponsoring the library. Scroll-linked motion is also a common performance trap: the HTTP Archive Web Almanac found non-composited, jank-prone animations on roughly 40% of mobile pages, so keep scrub and pin animating transforms (x, y, scale) rather than layout properties.
Modes

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.

When

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'
Alternatives

Use something else when

  • Pure entrance animation on load - use a normal tween, no ScrollTrigger needed
  • Element visibility detection without animation - use IntersectionObserver directly (smaller, no GSAP dep)
  • Scroll-snap behaviour - native CSS scroll-snap is better than ScrollTrigger
  • Smooth scrolling - that's Lenis or ScrollSmoother, not ScrollTrigger
In production

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
Used in components

See it running in production

FAQ

Common questions

Do I need to register ScrollTrigger before using it?
Yes. Add import { ScrollTrigger } from 'gsap/ScrollTrigger' and gsap.registerPlugin(ScrollTrigger) once at module load. In SSR contexts (Next.js), wrap the registration in if (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: true ties animation progress directly to scroll progress - instant, 1:1. scrub: 1 adds 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 set invalidateOnRefresh: true on individual triggers to also re-read the tween's from values. Pair with a debounced resize listener.
How do I make a ScrollTrigger fire once on enter and never again?
Pass once: true instead 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.