To trigger GSAP animations on scroll using ScrollTrigger, load GSAP + the ScrollTrigger plugin, register it with gsap.registerPlugin(ScrollTrigger), then attach a scrollTrigger config (with trigger, start, end, and optionally scrub or pin) to any gsap.to(), gsap.from(), or timeline. ScrollTrigger converts scroll position into animation progress — no scroll-event listeners, no manual math. On WordPress, you enqueue the two scripts and drop the same JS into a Bricks Builder Code element.
Why This Guide Exists
If you’ve searched for how to trigger GSAP animations on scroll using ScrollTrigger, you’ve probably already tried IntersectionObserver or a raw scroll event listener and hit the same wall: jank, re-triggering bugs, and animation math that falls apart the moment a client asks for “one more effect.” ScrollTrigger — GSAP’s scroll plugin — solves this at the library level, and as of the GSAP/Webflow merger, it ships free with the standard gsap package, no membership required.
This is a technical, implementation-first walkthrough. You’ll get the setup, the property table you’ll actually reference later, two full code examples (basic + advanced), a WordPress/Bricks Builder integration path, real example sites, and an FAQ block structured for both human readers and AI answer engines.
What Is GSAP ScrollTrigger?
ScrollTrigger is a plugin bundled with GSAP (GreenSock Animation Platform) that links any tween or timeline to the scroll position of the page (or a scrollable container). Instead of writing window.addEventListener('scroll', ...) and calculating percentages yourself, you describe where an element should start and end reacting to scroll, and ScrollTrigger handles the math, resize recalculation, and cleanup.
Three things make it different from CSS scroll-timeline or vanilla Intersection Observer code:
- Scrubbing — you can bind animation progress directly to scroll position (
scrub: true), so the animation moves backward when the user scrolls up. - Pinning — an element can be frozen in the viewport while a longer animation plays out underneath it.
- A single source of truth — start/end points, markers for debugging, and lifecycle callbacks (
onEnter,onLeave,onUpdate) all live in one config object.
GSAP works around countless browser inconsistencies, and it’s used on over 12 million sites, which is part of why ScrollTrigger — rather than a hand-rolled scroll listener — is the default choice for production scroll animation today.
ScrollTrigger vs. Other Scroll-Animation Approaches
| Approach | Setup effort | Scrub / reverse on scroll-up | Pinning support | Browser consistency | Best for |
|---|---|---|---|---|---|
| GSAP ScrollTrigger | Low (declarative config) | Yes, built-in (scrub) |
Yes, built-in (pin) |
Normalized across browsers | Complex, production scroll storytelling |
Native CSS scroll-timeline |
Low, but limited syntax | Yes, but CSS-only easing/logic | No native pinning | Inconsistent support across browsers | Simple fades/reveals, progressive enhancement |
| Intersection Observer (vanilla JS) | Medium–high (manual math) | No — you build it yourself | No — manual position: fixed hacks |
Consistent, but you own the edge cases | Lightweight “fade in once” effects |
scroll event listener |
High (manual throttling, math) | Yes, if you write it | No — manual only | You own all the quirks | Legacy projects, no dependencies allowed |
Takeaway: if the brief includes scrubbing, pinning, or a sequence of timed steps, ScrollTrigger removes an entire category of bugs you’d otherwise have to write yourself.
Prerequisites
- Basic HTML/CSS/JS knowledge
- A page where you control the
<head>or can enqueue scripts (plain HTML, WordPress, or a builder like Bricks) - GSAP core + ScrollTrigger loaded (both are now part of the same free
gsappackage — no separate “Club GreenSock” purchase needed)
Step-by-Step: How to Trigger GSAP Animations on Scroll Using ScrollTrigger
Step 1 — Load and register the plugin
Via CDN (fastest for testing or a Bricks Code element):
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script> <script> gsap.registerPlugin(ScrollTrigger); </script>
Via npm (for a build pipeline — React, Vue, or a custom theme):
npm install gsap
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);Registering the plugin isn’t optional — most bundlers tree-shake unused code, and without registerPlugin(), ScrollTrigger gets silently dropped from the build.
Step 2 — Write your first scroll-triggered animation
gsap.registerPlugin(ScrollTrigger);
gsap.from(".fade-box", {
opacity: 0,
y: 60,
duration: 1,
ease: "power2.out",
scrollTrigger: {
trigger: ".fade-box",
start: "top 80%", // animation starts when the top of .fade-box hits 80% down the viewport
end: "bottom 20%",
toggleActions: "play none none reverse"
}
});This is the core pattern behind almost every “fade/slide in as you scroll” effect you’ve seen on modern landing pages.
Step 3 — The ScrollTrigger properties you’ll actually use
| Property | What it does | Common value |
|---|---|---|
trigger |
The element whose position in the viewport starts/stops the animation | ".fade-box" |
start |
Where the trigger begins reacting, as "[trigger-position] [viewport-position]" |
"top 80%" |
end |
Where it stops reacting | "bottom 20%" |
scrub |
Ties animation progress directly to scroll position (true, or a number for “catch-up” delay) |
true or 1 |
pin |
Freezes the trigger element in place while the animation plays | true |
toggleActions |
Four actions for onEnter onLeave onEnterBack onLeaveBack |
"play none none reverse" |
markers |
Shows visual start/end lines — for local debugging only | true |
onUpdate |
Callback fired on every scroll update, receives the ScrollTrigger instance | (self) => console.log(self.progress) |
Step 4 — Advanced example: pinned section + scrubbed timeline
This is the pattern behind most “pin a section and animate through it” scroll stories:
gsap.registerPlugin(ScrollTrigger);
let tl = gsap.timeline({
scrollTrigger: {
trigger: "#story-section",
start: "top top",
end: "+=1200", // pin for 1200px of scroll distance
scrub: 1, // 1-second "catch up" for a smoother feel
pin: true,
anticipatePin: 1 // reduces the 1-frame flash some browsers show right at pin
}
});
tl.to("#story-title", { opacity: 0, y: -40 })
.from("#story-image", { scale: 0.8, opacity: 0 }, "<")
.to("#story-image", { rotateZ: 6, x: 80 });Because everything is on one timeline, the three steps play back-to-back as the user scrolls, and reverse cleanly if they scroll back up — no extra code needed.
Step 5 — Debug with markers, then remove them
scrollTrigger: {
trigger: ".fade-box",
start: "top 80%",
markers: true // shows colored start/end lines in dev — delete before shipping
}Always strip markers: true before pushing to production; it’s a debug-only visual aid.
Using GSAP ScrollTrigger in WordPress with Bricks Builder
Bricks Builder doesn’t ship GSAP by default, so the integration has two parts: load the library, then write the trigger logic in a Code element.
- Enqueue GSAP + ScrollTrigger in your child theme’s
functions.php(preferred over a page-level<script>tag, since it’s cached and loads consistently across the site):
function enqueue_gsap_scripts() {
wp_enqueue_script('gsap-core', 'https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js', [], null, true);
wp_enqueue_script('gsap-scrolltrigger', 'https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js', ['gsap-core'], null, true);
}
add_action('wp_enqueue_scripts', 'enqueue_gsap_scripts');
- Add a Bricks Code element (or the Bricks “Custom code” section under a Section/Container) at the bottom of the page and drop your ScrollTrigger logic in a
<script>tag, targeting the CSS classes you’ve already set on your Bricks elements:
<script>
document.addEventListener('DOMContentLoaded', function () {
gsap.registerPlugin(ScrollTrigger);
gsap.from(".brxe-block.reveal-on-scroll", {
opacity: 0,
y: 50,
duration: 0.9,
ease: "power2.out",
stagger: 0.15,
scrollTrigger: {
trigger: ".brxe-block.reveal-on-scroll",
start: "top 85%"
}
});
});
</script>
- In Bricks, give the elements you want to animate a custom CSS class (e.g.
reveal-on-scroll) via the element’s Style panel — this keeps your JS selectors stable even if you rearrange the layout later.
Performance note for Bricks/WordPress specifically: load GSAP core + ScrollTrigger only on pages that use them (Bricks lets you conditionally enqueue per template), and always call ScrollTrigger.refresh() after any Bricks popup, tab, or accordion interaction that changes page height — otherwise your start/end points drift out of sync with the new layout.
Real-World Example Sites and Demos
You don’t need to imagine what this looks like in production — these are good reference points:
- GSAP’s own Showcase (gsap.com/showcase) — curated, GreenSock-approved scroll and interaction demos.
- Awwwards’ GSAP tag (awwwards.com/websites/gsap) — award-winning agency and product sites built on GSAP, many using ScrollTrigger for pinned storytelling sections.
- “Made in Webflow: GSAP” gallery — cloneable, real production examples of scroll-triggered text reveals, marquees, and pinned sections you can inspect in DevTools.
- CodePen scroll-effect collections (search “GSAP ScrollTrigger” on CodePen) — hundreds of open-source pens showing horizontal scroll, scroll-scrubbed video, and SVG-morph-on-scroll patterns you can fork directly.
Studying two or three of these in DevTools — specifically their scrollTrigger config objects — is often faster than reading documentation, because you see real start/end values used at production scale.
Common Mistakes and Performance Tips
| Mistake | Why it hurts | Fix |
|---|---|---|
Animating top/left/width instead of transform/opacity |
Forces layout reflow on every scroll tick | Animate x, y, scale, opacity — GPU-accelerated |
Forgetting gsap.registerPlugin(ScrollTrigger) |
Plugin gets tree-shaken or silently ignored | Always register immediately after import |
Leaving markers: true in production |
Visible debug lines on the live site | Remove before deploy, or gate behind an env check |
Not calling ScrollTrigger.refresh() after dynamic content loads |
Start/end points miscalculate against the old page height | Call .refresh() after images load, accordions open, or AJAX content inserts |
| Too many separate ScrollTriggers on one page | Each one adds a scroll listener and recalculation cost | Group related animations into a single Timeline with one scrollTrigger config |
No will-change or reduced-motion handling |
Accessibility and mobile jank | Respect prefers-reduced-motion and test on mid-range Android devices |
FAQ
What is ScrollTrigger used for in GSAP? ScrollTrigger links a GSAP tween or timeline to scroll position so animations play, pause, reverse, or scrub based on where an element sits in the viewport — without manual scroll-event math.
Is GSAP ScrollTrigger free to use? Yes. Since GreenSock’s acquisition by Webflow, ScrollTrigger and all former “Club GreenSock” bonus plugins are included free in the standard gsap npm package and CDN build — no license purchase required for standard use.
How do I trigger a GSAP animation only once when scrolling? Set toggleActions: "play none none none" inside your scrollTrigger config, or add once: true if you only ever want it to fire a single time and never reverse.
Can ScrollTrigger animate on scroll up as well as scroll down? Yes — that’s what toggleActions and scrub control. "play none none reverse" plays forward on scroll-down and reverses on scroll-up automatically.
Does ScrollTrigger work with React, Vue, or a page builder like Bricks? Yes. In React/Vue, register the plugin inside a useEffect/onMounted hook and clean it up on unmount. In Bricks Builder or WordPress generally, enqueue the scripts site-wide and target your elements’ custom CSS classes from a Code element, as shown above.
Why isn’t my ScrollTrigger animation firing? The two most common causes: you forgot gsap.registerPlugin(ScrollTrigger), or your trigger selector doesn’t match an element that exists in the DOM at the time the script runs. Wrap your setup in DOMContentLoaded or your framework’s mount hook to rule out timing issues.
What’s the difference between scrub and toggleActions? toggleActions plays a fixed-duration animation once triggered (like a switch). scrub continuously ties the animation’s progress percentage to the scrollbar’s position (like a slider) — the animation moves forward and backward in real time as the user scrolls.
Wrapping Up
Once you understand that ScrollTrigger is really just “scroll position → animation progress,” the property table above is genuinely all you need for 90% of real projects — the rest is creative combination of scrub, pin, and toggleActions. Start with the basic fade-in pattern, get comfortable debugging with markers: true, then move to pinned timelines once single-element animations feel too simple for the brief.
If you’re implementing this inside WordPress with Bricks Builder, enqueue GSAP once at the theme level, keep your trigger logic in one Code element per template, and always re-run ScrollTrigger.refresh() after any layout-shifting interaction.

