Looking to create smooth, high-converting motion on your site without bloat or headaches? Webflow animations and GSAP give you studio-level control with production-grade performance. Below, we show when to use GSAP in Webflow, how to add it, and a clear ScrollTrigger walkthrough you can copy. You will get practical patterns, performance tips, and the exact steps our team uses in client builds.
Why use GSAP with Webflow
GSAP vs Webflow interactions - when to go custom
If you need precise timing, complex sequences, scroll-driven scenes, or component-level control that scales across pages, GSAP in Webflow is the right move. Use native Webflow Interactions for simple reveals and micro-animations. Switch to GSAP when your brief demands performance, flexibility, or reusable code that designers and developers can iterate on together.
Here is a simple way to decide:
- Use Webflow Interactions for single-element fades, slide-ins, and basic on-load or hover effects.
- Use Webflow animations and GSAP for scroll-driven storytelling, pinned sections, dynamic timelines, and component libraries that animate consistently across layouts.
- Use both when design needs a no-code baseline with targeted GSAP overrides for advanced effects.
Bonus: if you are scaling a design system, GSAP timelines reduce duplication and keep motion consistent. That makes QA faster and results steadier.
What ScrollTrigger adds (Webflow animations and GSAP)
ScrollTrigger connects scroll position to your animations. It lets you pin sections, scrub through timelines, snap to panels, and trigger effects when elements enter the viewport. Combined with Webflow animations and GSAP, it powers immersive stories that still load fast and remain controllable in code.
- Pin and scrub longform sections for product tours and feature explainers.
- Trigger staggered reveals only when a component appears in view.
- Sync animations to progress bars, counters, or videos.
For further context on best practices, check the official GSAP Webflow resources at gsap.com/resources/Webflow/.
Enable GSAP and plugins in your Webflow site
Toggle GSAP in site settings (SplitText, ScrollTrigger)
There are two reliable paths. If your account shows built-in options tied to Webflow Interactions powered by GSAP, you can toggle features in project settings and run advanced motion with minimal code. If not, you can still Add GSAP to Webflow via CDN links in the Head or Footer custom code fields.
Key points most teams miss:
- GSAP is free in Webflow for core and many plugins. ScrollTrigger is free. GSAP SplitText is a paid Club GreenSock plugin.
- Webflow GSAP free workflows typically cover ScrollTrigger plus standard easing and timeline features.
- For enterprise governance, centralize the library load in site settings, not page embeds.
Version and plugin considerations
Lock your GSAP version to avoid surprise changes. Test upgrades in a staging project. If you rely on GSAP plugins Webflow teams commonly use, like ScrollTrigger or SplitText, confirm licenses and load order.
- ScrollTrigger can live alongside native Webflow interactions without conflict if you scope selectors carefully.
- SplitText is great for kinetic headlines. Remember it requires a Club membership for production sites.
- Document your chosen version in your project readme so future editors do not upgrade by accident.
Quick setup: where to place GSAP code in Webflow
Site settings vs page settings vs embed - pros and cons
There are three common injection points, each with tradeoffs. For most builds, site-wide loading plus page-level init gives the best balance of control and performance.
- Site settings (Global Head/Footer): one source of truth, easy to version-lock, fewer load duplicates. Ideal for core GSAP and global helpers.
- Page settings: great for page-specific animations, reduces unused code on other pages, but can fragment logic if overused.
- Embed element: useful for component-scoped scripts in Templates or Symbols. Avoid heavy logic here to keep the Designer clean.
Best practices for class naming and scoping
Clean naming avoids selector bugs. Think “component__element--state.” Keep animation selectors as classes, not IDs, so you can reuse them across CMS items and components.
- Use data attributes like
data-animate="fade-in"
to initialize reusable modules. - Scope ScrollTrigger to the nearest section with
scroller
or containers if needed. - Avoid targeting Webflow’s auto-generated classes. Use your own semantic layer for stability.
Step-by-step ScrollTrigger tutorial (Webflow animations and GSAP)
Step 1: prep the page and target elements (class names, CMS slot)
Create a section for your story panel and add a sticky container inside. Assign classes like .story
for the wrapper and .panel
for each inner step. If you use the CMS, make sure each item outputs the same structure so your selectors work for every row.
- Wrapper:
.story
- Pinned container:
.story__pin
- Panels:
.panel
(repeatable via CMS)
Step 2: add GSAP and ScrollTrigger to the site (script placement)
Load GSAP and ScrollTrigger once globally. Then initialize per page. If you cannot use the site toggle, paste the following in Site settings > Custom Code > Footer:
<!-- Load GSAP core and ScrollTrigger --><script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script><script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/ScrollTrigger.min.js"></script><script> window.addEventListener('DOMContentLoaded', function() { if (window.gsap && window.ScrollTrigger) { gsap.registerPlugin(ScrollTrigger); } });</script>
Step 3: basic ScrollTrigger example (pin and scrub) - copy-paste code
This example pins a section and scrubs through panels. It is a simple pattern you can reuse for product tours or feature highlights. Drop this in your page settings Footer or an Embed at the bottom of the page.
<script> document.addEventListener('DOMContentLoaded', function() { if (!window.gsap || !window.ScrollTrigger) return; gsap.registerPlugin(ScrollTrigger); // Set start states gsap.set('.panel', { autoAlpha: 0, y: 50 }); // Timeline that scrubs as you scroll const tl = gsap.timeline({ scrollTrigger: { trigger: '.story', start: 'top top', end: '+=200%', scrub: true, pin: '.story__pin', anticipatePin: 1 } }); // Reveal each panel in sequence document.querySelectorAll('.panel').forEach((panel, i) => { tl.to(panel, { autoAlpha: 1, y: 0, duration: 0.6, ease: 'power2.out' }, i); // Optional fade previous panel as next appears if (i > 0) { tl.to(document.querySelectorAll('.panel')[i - 1], { autoAlpha: 0.2, duration: 0.3 }, i); } }); });</script>
Step 4: make it responsive and media queries
Use GSAP’s matchMedia
to keep interactions responsive. Create different ScrollTrigger setups for mobile and desktop. You can also disable heavy motion for small screens while keeping quick reveals.
<script> document.addEventListener('DOMContentLoaded', function() { if (!window.gsap || !window.ScrollTrigger) return; gsap.registerPlugin(ScrollTrigger); const mm = gsap.matchMedia(); mm.add({ isDesktop: '(min-width: 992px)', isTablet: '(min-width: 768px) and (max-width: 991px)', isMobile: '(max-width: 767px)' }, (ctx) => { const { isDesktop, isTablet, isMobile } = ctx.conditions; // Common gsap.set('.panel', { autoAlpha: 0, y: 40 }); // Desktop: pin and scrub if (isDesktop) { const tl = gsap.timeline({ scrollTrigger: { trigger: '.story', start: 'top top', end: '+=250%', scrub: true, pin: '.story__pin' } }); document.querySelectorAll('.panel').forEach((p, i) => { tl.to(p, { autoAlpha: 1, y: 0, duration: 0.5 }, i); }); } // Tablet/Mobile: simpler triggers, no pin if (isTablet || isMobile) { document.querySelectorAll('.panel').forEach((p) => { gsap.to(p, { autoAlpha: 1, y: 0, duration: 0.5, scrollTrigger: { trigger: p, start: 'top 75%', toggleActions: 'play none none reverse' } }); }); } }); });</script>
Step 5: clean up and destroy - avoid memory leaks
When components re-render or pages change, kill triggers and revert matchMedia scopes. This prevents stacked animations and janky scroll. Use guards to avoid double initialization when editors duplicate sections.
<script> // Example of a safe init with cleanup (function() { let inited = false; function init() { if (inited) return; inited = true; gsap.registerPlugin(ScrollTrigger); // ... your ScrollTrigger code // Return a cleanup function return () => { ScrollTrigger.getAll().forEach(t => t.kill()); if (gsap.globalTimeline) gsap.globalTimeline.clear(); }; } let cleanup; document.addEventListener('DOMContentLoaded', function() { cleanup = init(); }); // Optional: re-init on Webflow page renders or dynamic injections document.addEventListener('animation:reinit', function() { if (cleanup) cleanup(); cleanup = init(); }); })();</script>
Animating CMS content and reusable patterns
Initialising animations for CMS lists and pagination
When using CMS lists or built-in pagination, items can appear after initial page load. A lightweight MutationObserver helps you catch new nodes and initialize animations on demand. This keeps Webflow animations and GSAP fast on long lists.
<script> document.addEventListener('DOMContentLoaded', function() { if (!window.gsap || !window.ScrollTrigger) return; gsap.registerPlugin(ScrollTrigger); const list = document.querySelector('[data-cms="articles"]'); if (!list) return; function initItem(el) { gsap.fromTo(el, { y: 20, autoAlpha: 0 }, { y: 0, autoAlpha: 1, duration: 0.4, scrollTrigger: { trigger: el, start: 'top 85%' } }); } list.querySelectorAll('.cms-item').forEach(initItem); const obs = new MutationObserver((muts) => { muts.forEach(m => m.addedNodes.forEach(n => { if (n.nodeType === 1 && n.classList.contains('cms-item')) initItem(n); })); }); obs.observe(list, { childList: true, subtree: true }); });</script>
Creating reusable modules with data attributes
Data attributes make your motion system portable. Define a set of behaviors and apply them anywhere by adding data-animate
. Editors can reuse animations without touching code, and your timelines stay predictable.
- Add
data-animate="fade-in"
ordata-animate="stagger-children"
to containers. - Write one initializer that scans the DOM and attaches the right effect.
- Document options so your team can scale Webflow animations examples with confidence.
<script> const behaviors = { 'fade-in': (el) => gsap.from(el, { autoAlpha: 0, y: 20, duration: 0.5, scrollTrigger: { trigger: el, start: 'top 85%' } }), 'stagger-children': (el) => gsap.from(el.children, { autoAlpha: 0, y: 10, duration: 0.4, stagger: 0.08, scrollTrigger: { trigger: el, start: 'top 85%' } }) }; function initBehaviors(scope = document) { scope.querySelectorAll('[data-animate]').forEach((el) => { const type = el.getAttribute('data-animate'); if (behaviors[type]) behaviors[type](el); }); } document.addEventListener('DOMContentLoaded', () => { initBehaviors(); });</script>
Performance and accessibility checklist for GSAP in Webflow
Performance: lazy init, defer, and reducing payload
Keep performance tight. Your animations should feel invisible until they capture attention. Use lazy strategies and reduce payload where possible.
- Load GSAP from site settings, defer initialization until
DOMContentLoaded
. - Code-split heavy modules and only initialize on pages that need them.
- Replace large GIFs with video or Lottie and avoid layout thrash by animating transforms and opacity.
- Test Core Web Vitals and simulate low-end devices before launch. Aim for stable CLS and smooth INP.
For a broader performance playbook, our Core Web Vitals guide shows how we tune speed for growth-focused sites.
Accessibility: prefers-reduced-motion and keyboard support (Webflow animations and GSAP accessibility)
Not everyone wants motion. Respect user settings and keep controls keyboard-friendly. Webflow animations and GSAP should enhance the experience without blocking interaction or causing fatigue.
- Use
@media (prefers-reduced-motion: reduce)
to disable long scrubs. - Never lock the scroll without offering a bypass.
- For pinned scenes, ensure focusable elements remain reachable by keyboard.
/* CSS */@media (prefers-reduced-motion: reduce) { .anim-intensive { animation: none !important; transition: none !important; }}/* JS guard */const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;if (!reduceMotion) { // run ScrollTrigger-heavy sequences}
KPIs to track - pagespeed and conversion metrics
Motion should move the numbers that matter. Tie your Webflow animations examples to KPIs and track impact.
- Speed: LCP under 2.5s, CLS under 0.1, INP under 200 ms.
- Engagement: scroll depth on animated sections, time on page, interaction rate.
- Conversion: CTR on animated CTAs, add-to-cart rate, form completion rate.
- Quality: error logs, JS exceptions, and ScrollTrigger warnings at zero.
Common problems and debugging tips
Selector and init timing errors
Most issues come from missing elements at init time or too-broad selectors. Initialize after the DOM is ready and verify your triggers exist before creating ScrollTriggers.
- Wrap setups in
DOMContentLoaded
and use guard clauses when querying elements. - Use
markers: true
while debugging ScrollTrigger start and end points. - Check stacking contexts and transforms on parents that could break
position: sticky
.
// Example guardconst section = document.querySelector('.story');if (!section) { console.warn('No .story found'); return; }
Cross-page and SPA issues
If your site uses page transitions or dynamic injections, rebuild triggers on each navigation and destroy the old ones. This prevents duplicate timelines and event listeners.
- Kill all triggers on route change:
ScrollTrigger.getAll().forEach(t => t.kill())
. - Namespace your events and remove them on cleanup.
- Fire a custom event like
animation:reinit
after each page mount and re-run initializers.
Low-code alternatives and when to use them
Webflow interactions and in-designer GSAP features
For teams that want visual control, the latest Interactions updates bring more power natively. Webflow Interactions powered by GSAP means you can blend no-code timelines with surgical GSAP overrides when needed.
See the official announcement for details on capabilities and roadmap: webflow.com/updates/introducing-webflow-interactions-powered-by-gsap.
Third-party boosters and plugin vs hand-code tradeoffs
Third-party boosters can speed up common patterns like marquee text or counters. Hand-coding keeps your bundle lean and your motion system consistent. If your team prefers design-first, start in the Designer and sprinkle GSAP. If you need reusable modules across a large site, build a small GSAP utility layer.
- Hand-code when performance, version control, and reusability are priorities.
- Use plugins for specialized effects like SplitText or physics-based motion where it saves time.
- Document your approach so future editors can extend safely.
Talk to 6th Man to ship high-impact Webflow animations fast
What we deliver - scoped animation packages and editor docs
Need help turning a motion concept into a fast, accessible build? Our senior team ships Webflow animations and GSAP that boost engagement without hurting speed. We scope cleanly, code defensively, and hand over editor docs your team can actually use.
- Reusable GSAP modules with data attributes and matchMedia patterns.
- ScrollTrigger sections that clarify product value and increase conversions.
- Performance and accessibility guardrails baked in from day one.
See how we work and what we deliver on our Webflow website development experts page, explore case studies, and how we build conversion-focused landing pages for growth teams.
Book a quick intake (link to contact)
If you want a hands-on partner to plan, build, and measure Webflow animations and GSAP, let’s chat. Share your goals and we will suggest a right-sized approach in minutes. Contact 6th Man to get started.