Sign In

Access the AI Animation Generator and premium effects

or
Don't have an account? Create one

You've Hit Your Limit

Upgrade your plan to keep generating animations and unlock premium effects.

Monthly Yearly Save 17%
Regular
$12/mo
For individual developers
  • 50 AI generations per month
  • All text animation effects
  • Code export & copy
Integration Guide

GSAP + Webflow

Supercharge your Webflow projects with the industry-leading animation library. Master custom code integration, ScrollTrigger, page transitions, and CMS animations.

Chapter 1

Why GSAP in Webflow?

Webflow is a powerful visual web builder, and its native Interactions are great for basic animations. But GSAP is the professional-grade animation library used by Disney, Apple, Google, and thousands of agencies worldwide. Here's why you should add GSAP to your Webflow toolkit.

Webflow acquired GreenSock in 2024 — all GSAP plugins are now 100% FREE

Feature Webflow Interactions GSAP
Visual Interface Yes Code-based
Scroll Animations Yes More powerful
Timeline Sequencing Limited Advanced
Text Splitting No SplitText plugin
SVG Morphing No MorphSVG plugin
Physics Simulations No Draggable, Physics
Custom Easing Limited presets Full control
Performance Good Optimized
Code Control None Full flexibility
Community Ecosystem Webflow-specific Massive (web-wide)

When to Use Each

Use Webflow Interactions for:

  • Simple hover states
  • Basic scroll reveals
  • Quick CMS animations
  • Designer-mode prototyping

Use GSAP for:

  • Complex timelines
  • Advanced scroll interactions
  • Text and SVG animations
  • High-performance sites

Benefits of Using GSAP

Performance

GSAP uses requestAnimationFrame and GPU acceleration for silky-smooth 60fps animations even with thousands of animated properties.

Full Control

Write JavaScript code with complete control over timing, sequencing, easing, and responsive behavior.

Rich Plugin Ecosystem

ScrollTrigger, SplitText, MorphSVG, Draggable, Physics, and more—all free in Webflow.

Massive Community

Thousands of tutorials, CodePen examples, and a supportive community across the entire web industry.

Chapter 2

Setup & Installation

Adding GSAP to your Webflow project takes less than 5 minutes. You have three options: use Webflow's built-in GSAP toggle, add CDN scripts via Site Settings, or use page-level custom code.

Option 1: Webflow's GSAP Plugin Toggle (Recommended)

In Webflow (late 2024+), there's a toggle in Site Settings to enable GSAP plugins directly. This is the easiest method.

01

Go to Site Settings

In your Webflow dashboard, click the Settings icon (gear) → Integrations

02

Enable GSAP Plugins

Look for "GSAP" toggle and enable ScrollTrigger, SplitText, and any other plugins you need

03

Start Coding

GSAP is now globally available in your custom code. Use Webflow.push() to run code when the DOM is ready.

Option 2: Manual CDN Installation

If you're on an older Webflow plan or need a specific version, add scripts manually via Site Settings → Custom Code.

01

Add to Site Settings - Head

Paste this in Site Settings → Custom Code → Head (optional, but good for early loading)

<!-- GSAP Library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
02

Add Plugins (Before </body>)

Go to Site Settings → Custom Code → Before </body> and add:

<!-- ScrollTrigger Plugin -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>

<!-- SplitText Plugin (Optional) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/SplitText.min.js"></script>

<!-- MorphSVG Plugin (Optional) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/MorphSVGPlugin.min.js"></script>
03

Register Plugins

Add this code to register the plugins with GSAP:

gsap.registerPlugin(ScrollTrigger, SplitText, MorphSVGPlugin);

Pro Tip

Always load GSAP core before plugins. Place the main gsap.min.js script first, then plugin scripts, then registration code.

Chapter 3

The Webflow.push() DOM Ready Pattern

This is critical when using GSAP in Webflow. Webflow has a specific initialization pipeline, and you must wait for the DOM to be fully ready before accessing Webflow elements.

Why Webflow.push()?

Webflow includes a global Webflow object with a push() method. This ensures your code runs after Webflow has initialized its classes, components, and interactions. If you run GSAP code before this happens, selectors might fail to find elements.

Basic Pattern

Webflow Custom Code — Before </body>
Webflow.push(function() {
  // Your GSAP code runs here after Webflow is ready
  gsap.to('.my-element', { duration: 1, opacity: 1 });
});

Multiple Webflow.push() Calls

You can call Webflow.push() multiple times. Each callback will execute in order when Webflow is ready.

Webflow Custom Code — Before </body>
// Initialize animations
Webflow.push(function() {
  console.log('Webflow is ready!');

  // Hero animation
  gsap.from('.wf-hero h1', {
    duration: 1,
    opacity: 0,
    y: 30,
  });
});

// Initialize ScrollTrigger
Webflow.push(function() {
  // ScrollTrigger code here
  gsap.registerPlugin(ScrollTrigger);
});

Advanced: Reusable Functions

For larger projects, define functions outside Webflow.push() and call them inside:

Webflow Custom Code — Before </body>
// Define animation functions
function initHeroAnimation() {
  gsap.from('.wf-hero h1', {
    duration: 1.2,
    opacity: 0,
    y: 50,
    ease: 'power3.out'
  });
}

function initScrollAnimations() {
  gsap.registerPlugin(ScrollTrigger);

  gsap.utils.toArray('.wf-fade-in').forEach(element => {
    gsap.from(element, {
      scrollTrigger: {
        trigger: element,
        start: 'top 80%',
        markers: false
      },
      opacity: 0,
      y: 30,
      duration: 0.8
    });
  });
}

// Run when Webflow is ready
Webflow.push(function() {
  initHeroAnimation();
  initScrollAnimations();
});

Alternative: DOMContentLoaded

If Webflow.push() isn't available on your plan, use native DOM events:

Fallback Pattern
// Fallback if Webflow.push() not available
if (window.Webflow && typeof Webflow.push === 'function') {
  Webflow.push(function() {
    // GSAP code here
  });
} else {
  document.addEventListener('DOMContentLoaded', function() {
    // GSAP code here
  });
}

Critical Warning

Never run GSAP code in a script tag at the bottom of custom code without Webflow.push(). The DOM elements might not exist yet, causing your selectors to fail silently.

Chapter 4

Basic Animations in Webflow

Webflow adds special class prefixes and data attributes that make it easy to target elements. Let's learn how to select and animate them with GSAP.

Targeting Webflow Elements

Webflow uses .w- prefixes for built-in classes. You can target these like any CSS selector.

Webflow Custom Code — Targeting Elements
Webflow.push(function() {
  // Target by custom class
  gsap.to('.my-box', { duration: 1, opacity: 1 });

  // Target by Webflow navbar
  gsap.to('.w-nav-menu', { duration: 0.5, height: 'auto' });

  // Target by element ID
  gsap.to('#my-hero', { duration: 1, y: -50 });

  // Target multiple elements
  gsap.to('.feature-card', {
    duration: 0.8,
    opacity: 1,
    stagger: 0.2  // 0.2s delay between each element
  });
});

Using Data Attributes for Reusable Animations

Add custom data attributes to your Webflow elements to create flexible, reusable animations:

HTML in Webflow Designer
<!-- Add data attributes in Webflow Designer -->
<div class="card" data-animation="fade-up">
  <h3>Card Title</h3>
</div>
Webflow Custom Code — Before </body>
Webflow.push(function() {
  // Target elements by data attribute
  const fadeUpElements = document.querySelectorAll('[data-animation="fade-up"]');

  fadeUpElements.forEach((element, index) => {
    gsap.from(element, {
      opacity: 0,
      y: 30,
      duration: 0.8,
      delay: index * 0.1,
      ease: 'power2.out'
    });
  });
});

Batch Animations with gsap.utils.toArray()

For cleaner code, use GSAP's built-in gsap.utils.toArray() to select multiple elements:

Webflow Custom Code — Before </body>
Webflow.push(function() {
  // Animate all feature cards with stagger
  gsap.utils.toArray('.feature-card').forEach((card, index) => {
    gsap.from(card, {
      opacity: 0,
      y: 50,
      duration: 0.8,
      delay: index * 0.15,
      ease: 'power3.out'
    });
  });

  // Alternative: Use stagger parameter (simpler)
  gsap.from('.feature-card', {
    opacity: 0,
    y: 50,
    duration: 0.8,
    stagger: 0.15,
    ease: 'power3.out'
  });
});

Common Webflow Animation Patterns

Button Hover Animation
Webflow.push(function() {
  const buttons = gsap.utils.toArray('a.button');

  buttons.forEach(button => {
    // Mouse enter
    button.addEventListener('mouseenter', () => {
      gsap.to(button, {
        scale: 1.05,
        duration: 0.3,
        overwrite: 'auto'
      });
    });

    // Mouse leave
    button.addEventListener('mouseleave', () => {
      gsap.to(button, {
        scale: 1,
        duration: 0.3,
        overwrite: 'auto'
      });
    });
  });
});
Counter Animation
Webflow.push(function() {
  // Animate numbers from 0 to target
  gsap.utils.toArray('[data-count]').forEach(element => {
    const targetValue = parseInt(element.dataset.count);

    gsap.to(element, {
      innerHTML: Math.floor(targetValue),
      duration: 2,
      snap: { innerHTML: 1 },
      ease: 'power2.out',
      scrollTrigger: {
        trigger: element,
        start: 'top 80%'
      }
    });
  });
});
Staggered List Animation
Webflow.push(function() {
  // Animate list items on scroll
  gsap.from('.list-item', {
    opacity: 0,
    x: -30,
    duration: 0.6,
    stagger: 0.1,
    scrollTrigger: {
      trigger: '.list-container',
      start: 'top 80%'
    }
  });
});

Tip

Use overwrite: 'auto' when combining hover animations with scroll triggers to prevent conflicts.

Chapter 5

ScrollTrigger in Webflow

ScrollTrigger is the gold standard for scroll-based animations. It's more powerful than Webflow's native scroll interactions and handles complex scenarios beautifully.

Basic ScrollTrigger Setup

First, ensure ScrollTrigger is registered with GSAP:

Register ScrollTrigger
Webflow.push(function() {
  // Register the ScrollTrigger plugin
  gsap.registerPlugin(ScrollTrigger);

  // Now you can use ScrollTrigger in your animations
});

Fade-In on Scroll

The most common use case: elements fade in as they scroll into view.

Webflow Custom Code — Before </body>
Webflow.push(function() {
  gsap.registerPlugin(ScrollTrigger);

  // Fade in sections as they scroll into view
  gsap.from('.wf-section', {
    opacity: 0,
    y: 50,
    duration: 0.8,
    stagger: 0.2,
    scrollTrigger: {
      trigger: '.wf-section',
      start: 'top 80%',     // When top of element is 80% down the viewport
      end: 'top 50%',       // When top of element is 50% down
      // markers: true      // Uncomment to debug (shows start/end points)
    }
  });
});

Pinning Sections

Pin a section to the viewport while animating content inside it:

Pinned Section Animation
Webflow.push(function() {
  gsap.registerPlugin(ScrollTrigger);

  gsap.to('.hero-content', {
    opacity: 0.3,
    y: -100,
    duration: 1,
    scrollTrigger: {
      trigger: '.hero-section',
      start: 'top top',
      end: 'bottom top',
      scrub: 1,              // Smooth scrubbing (1 = 1 second delay)
      pin: true,             // Pin the section
      pinSpacing: true       // Account for pinned element height
    }
  });
});

Scrub Animations (Timeline-based Scroll)

Scrub animations are tied directly to scroll position—perfect for revealing content:

Scrub Animation
Webflow.push(function() {
  gsap.registerPlugin(ScrollTrigger);

  // Create a timeline that plays as you scroll
  const tl = gsap.timeline({
    scrollTrigger: {
      trigger: '.timeline-section',
      start: 'top center',
      end: 'bottom center',
      scrub: true,           // true = instant, 1-3 = smooth
      markers: false
    }
  });

  tl.from('.step-1', { opacity: 0, y: 50, duration: 1 })
    .from('.step-2', { opacity: 0, y: 50, duration: 1 }, '-=0.5')
    .from('.step-3', { opacity: 0, y: 50, duration: 1 }, '-=0.5');
});

Parallax Background

Create depth with parallax scrolling:

Parallax Effect
Webflow.push(function() {
  gsap.registerPlugin(ScrollTrigger);

  // Background image moves slower than foreground
  gsap.to('.parallax-bg', {
    y: -200,
    ease: 'none',
    scrollTrigger: {
      trigger: '.parallax-section',
      start: 'top top',
      end: 'bottom top',
      scrub: 1
    }
  });
});

ScrollTrigger Best Practices in Webflow

Do

  • Use ScrollTrigger for scroll-heavy sites
  • Refresh on window resize
  • Set proper start/end positions
  • Test on mobile devices

Don't

  • Use huge scrub values (causes lag)
  • Pin too many elements at once
  • Ignore Webflow Interactions conflicts
  • Skip performance testing

Learn more: See our dedicated ScrollTrigger Guide

Chapter 6

Animating Webflow CMS Collections

The main challenge with CMS items is that they're rendered dynamically. You need to target them correctly and handle cases where items load after the initial page.

Understanding Webflow CMS Classes

Webflow automatically adds these classes to CMS elements:

Webflow CMS DOM Structure
<!-- Webflow auto-generates these classes -->
<div class="w-dyn-items">           <!-- Container for all items -->
  <div class="w-dyn-item">           <!-- Individual CMS item -->
    <div class="card">
      <h3 class="card-title">Item Title</h3>
      <p class="card-text">Description</p>
    </div>
  </div>
  <div class="w-dyn-item">
    <!-- More items... -->
  </div>
</div>

Basic CMS Animation Pattern

Animate CMS Items on Scroll
Webflow.push(function() {
  gsap.registerPlugin(ScrollTrigger);

  // Select all CMS items and animate them
  gsap.from('.w-dyn-item', {
    opacity: 0,
    y: 30,
    duration: 0.6,
    stagger: 0.15,
    scrollTrigger: {
      trigger: '.w-dyn-items',
      start: 'top 80%'
    }
  });
});

Handling Dynamic Content with MutationObserver

If your CMS items load dynamically (e.g., infinite scroll, pagination), use MutationObserver to detect and animate new items:

MutationObserver for Dynamic Items
Webflow.push(function() {
  gsap.registerPlugin(ScrollTrigger);

  function animateCMSItems() {
    gsap.from('.w-dyn-item', {
      opacity: 0,
      y: 30,
      duration: 0.6,
      stagger: 0.1,
      overwrite: 'auto'
    });
  }

  // Animate initial items
  animateCMSItems();

  // Watch for new items
  const observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
      if (mutation.addedNodes.length) {
        animateCMSItems();
        ScrollTrigger.refresh(); // Update scroll triggers
      }
    });
  });

  observer.observe(document.querySelector('.w-dyn-items'), {
    childList: true
  });
});

Staggered CMS Item Entry Animation

Create a timeline effect where items appear in sequence as the user scrolls:

Staggered Timeline
Webflow.push(function() {
  gsap.registerPlugin(ScrollTrigger);

  const items = gsap.utils.toArray('.w-dyn-item');

  const tl = gsap.timeline({
    scrollTrigger: {
      trigger: '.w-dyn-items',
      start: 'top 70%',
      end: 'center center'
    }
  });

  items.forEach((item, index) => {
    tl.from(item, {
      opacity: 0,
      x: -50,
      duration: 0.5
    }, index * 0.1);
  });
});

Animating Individual CMS Fields

Target specific fields within CMS items:

Field-Level Animation
Webflow.push(function() {
  gsap.registerPlugin(ScrollTrigger);

  // Animate only the title within each CMS item
  gsap.from('.w-dyn-item .card-title', {
    opacity: 0,
    y: 20,
    duration: 0.5,
    stagger: 0.1,
    scrollTrigger: {
      trigger: '.w-dyn-items',
      start: 'top 80%'
    }
  });

  // Then animate the description slightly after
  gsap.from('.w-dyn-item .card-text', {
    opacity: 0,
    y: 20,
    duration: 0.5,
    stagger: 0.1,
    delay: 0.2,
    scrollTrigger: {
      trigger: '.w-dyn-items',
      start: 'top 80%'
    }
  });
});

CMS Best Practice

Always use .w-dyn-item and .w-dyn-items selectors for CMS items, not custom classes. This ensures compatibility across all CMS configurations.

Chapter 7

Page Transitions with GSAP

Create smooth transitions between Webflow pages with exit and enter animations. This requires a bit more setup but feels incredibly polished.

Understanding the Flow

When a user clicks a link:

  1. Exit animation plays on current page
  2. Navigate to new page
  3. Enter animation plays on new page

Simple Page Transition Pattern

Use preventDefault() and AJAX-like page loads:

Basic Page Transition
Webflow.push(function() {
  // Fade out and navigate
  document.querySelectorAll('a').forEach(link => {
    link.addEventListener('click', function(e) {
      // Don't intercept external links or anchors
      if (this.hostname === location.hostname &&
          !this.href.includes('#') &&
          !this.classList.contains('no-transition')) {

        e.preventDefault();
        const href = this.href;

        // Fade out animation
        gsap.to('body', {
          opacity: 0,
          duration: 0.5,
          onComplete: () => {
            window.location.href = href;
          }
        });
      }
    });
  });

  // Fade in on page load
  gsap.from('body', {
    opacity: 0,
    duration: 0.5
  });
});

Advanced: Exit & Enter Animations

Separate animations for leaving and entering pages:

Exit & Enter Pattern
Webflow.push(function() {
  // Exit animation when leaving page
  function exitAnimation() {
    return gsap.to('main', {
      opacity: 0,
      y: 50,
      duration: 0.5,
      ease: 'power2.in'
    });
  }

  // Enter animation when arriving on page
  function enterAnimation() {
    return gsap.from('main', {
      opacity: 0,
      y: 50,
      duration: 0.5,
      ease: 'power2.out'
    });
  }

  // Hook up exit animation to links
  document.querySelectorAll('a:not(.no-transition)').forEach(link => {
    link.addEventListener('click', function(e) {
      if (this.hostname === location.hostname &&
          !this.href.includes('#')) {
        e.preventDefault();
        const href = this.href;

        exitAnimation().then(() => {
          window.location.href = href;
        });
      }
    });
  });

  // Play enter animation on page load
  enterAnimation();
});

Using a Preloader

Show a preloader while the page loads:

Preloader Pattern
Webflow.push(function() {
  const preloader = document.querySelector('.page-preloader');

  // Animate preloader in on transition
  function showPreloader() {
    return gsap.to(preloader, {
      opacity: 1,
      pointerEvents: 'auto',
      duration: 0.3
    });
  }

  // Hide preloader when page loads
  function hidePreloader() {
    return gsap.to(preloader, {
      opacity: 0,
      pointerEvents: 'none',
      duration: 0.5,
      delay: 0.5
    });
  }

  // On page load, hide preloader
  window.addEventListener('load', () => {
    hidePreloader();
  });

  // On link click, show preloader and transition
  document.querySelectorAll('a:not(.no-transition)').forEach(link => {
    link.addEventListener('click', function(e) {
      if (this.hostname === location.hostname &&
          !this.href.includes('#')) {
        e.preventDefault();

        showPreloader().then(() => {
          setTimeout(() => {
            window.location.href = this.href;
          }, 300);
        });
      }
    });
  });
});

Note

For production page transitions, consider using barba.js, a lightweight AJAX library designed for smooth page transitions. It pairs beautifully with GSAP and handles many edge cases automatically.

Add class no-transition to external links, download buttons, and form submits to skip the transition.

Chapter 8

Advanced Animation Patterns

Now that you've mastered the basics, let's explore advanced techniques used by top agencies.

Horizontal Scroll Section

Horizontal Scroll
Webflow.push(function() {
  gsap.registerPlugin(ScrollTrigger);

  const container = document.querySelector('.h-scroll-container');
  const cards = container.querySelectorAll('.card');
  const totalScroll = cards.length * 300; // Each card is ~300px

  gsap.to(container, {
    x: -totalScroll,
    ease: 'none',
    scrollTrigger: {
      trigger: '.h-scroll-section',
      start: 'top top',
      end: 'bottom top',
      scrub: 1,
      pin: true,
      markers: false
    }
  });
});

Text Split Animation (SplitText)

SplitText Character Animation
Webflow.push(function() {
  gsap.registerPlugin(SplitText);

  const headings = gsap.utils.toArray('h2');

  headings.forEach(heading => {
    const splitText = new SplitText(heading, {
      type: 'chars'
    });

    gsap.from(splitText.chars, {
      opacity: 0,
      y: 20,
      duration: 0.8,
      stagger: 0.05,
      scrollTrigger: {
        trigger: heading,
        start: 'top 80%'
      }
    });
  });
});

SVG Morphing with MorphSVG

SVG Morph Animation
Webflow.push(function() {
  gsap.registerPlugin(MorphSVGPlugin);

  const shape = document.querySelector('.morph-shape');

  gsap.to(shape, {
    attr: {
      d: document.querySelector('.morph-target').getAttribute('d')
    },
    duration: 2,
    ease: 'power2.inOut',
    scrollTrigger: {
      trigger: '.morph-section',
      start: 'top center',
      end: 'bottom center',
      scrub: 1
    }
  });
});

Lottie + GSAP Integration

Lottie Animation Control
Webflow.push(function() {
  gsap.registerPlugin(ScrollTrigger);

  // Control Lottie playback with scroll
  const lottieContainer = document.querySelector('#lottie-animation');

  // Make sure Lottie is loaded first
  if (window.bodymovin && lottieContainer.innerHTML) {
    gsap.to(window, {
      scrollTrigger: {
        trigger: '.lottie-section',
        start: 'top center',
        end: 'bottom center',
        scrub: 1,
        onUpdate: (self) => {
          // Control frame based on progress (0-1)
          const frame = Math.round(self.progress * 100);
          // Call Lottie playback method if available
        }
      }
    });
  }
});

Custom Cursor

Custom Cursor Follow
Webflow.push(function() {
  const cursor = document.querySelector('.custom-cursor');

  document.addEventListener('mousemove', (e) => {
    gsap.to(cursor, {
      x: e.clientX - 10,  // Center the cursor
      y: e.clientY - 10,
      duration: 0.1,
      overwrite: 'auto'
    });
  });

  // Make cursor larger on hover
  document.querySelectorAll('a, button').forEach(el => {
    el.addEventListener('mouseenter', () => {
      gsap.to(cursor, { scale: 1.5, duration: 0.2 });
    });

    el.addEventListener('mouseleave', () => {
      gsap.to(cursor, { scale: 1, duration: 0.2 });
    });
  });
});

Pro Tip

Always test advanced animations on both desktop and mobile. Some effects (like custom cursors or horizontal scrolling) need mobile alternatives.

Chapter 9

Performance & Debugging

GSAP is incredibly performant, but poor implementation can slow down your site. Here's how to optimize and troubleshoot.

Performance Best Practices

Use will-change
/* In your CSS file */
.animate-element {
  will-change: opacity, transform;
}

/* Or add it dynamically with GSAP */
Webflow.push(function() {
  gsap.set('.animate-element', {
    willChange: 'opacity, transform'
  });

  // Then animate
  gsap.to('.animate-element', {
    opacity: 0.5,
    duration: 1
  });
});

Debugging GSAP in Webflow

Debug Tips
Webflow.push(function() {
  // Check if elements exist
  const elements = document.querySelectorAll('.my-element');
  console.log('Found elements:', elements.length);

  // Log GSAP version
  console.log('GSAP Version:', gsap.version);

  // Check if plugins are registered
  console.log('Plugins:', gsap.plugins());

  // Enable ScrollTrigger debugging
  gsap.registerPlugin(ScrollTrigger);
  gsap.to('.my-element', {
    y: 50,
    scrollTrigger: {
      trigger: '.my-element',
      markers: true  // Show start/end markers
    }
  });

  // Log animation timeline
  const tl = gsap.timeline();
  tl.to('.element-1', { opacity: 1 });
  console.log('Timeline:', tl);
});

Common Issues & Fixes

Selector Not Finding Elements

Make sure you're using the correct class names and that Webflow.push() is running after the DOM is ready. Check browser console for errors.

ScrollTrigger Not Triggering

Refresh ScrollTrigger after content loads: ScrollTrigger.refresh()

Conflicts with Webflow Interactions

Don't animate the same element with both GSAP and Webflow Interactions. Use GSAP for all custom animations.

Animations Lag on Mobile

Reduce stagger values, simplify animations, and test on actual devices. Desktop is not mobile.

Performance Monitoring

Monitor Animation Performance
Webflow.push(function() {
  // Monitor FPS during animations
  let lastTime = performance.now();
  let fps = 0;

  const checkFPS = () => {
    const now = performance.now();
    const delta = now - lastTime;
    fps = Math.round(1000 / delta);
    console.log('FPS:', fps);
    lastTime = now;
  };

  // Check FPS during scroll
  window.addEventListener('scroll', checkFPS, { passive: true });

  // Clean up after animations complete
  gsap.to('.element', {
    opacity: 1,
    duration: 2,
    onComplete: () => {
      window.removeEventListener('scroll', checkFPS);
      console.log('Animation complete');
    }
  });
});

Test in Multiple Browsers

GSAP works across all modern browsers, but always test in Chrome, Firefox, Safari, and Edge to catch edge cases.

Frequently Asked Questions

Yes! As of 2024, Webflow acquired GreenSock (the company behind GSAP), making all GSAP plugins completely free for use in Webflow projects. This includes ScrollTrigger, SplitText, MorphSVG, Draggable, and more.

GSAP and Webflow Interactions can coexist, but avoid animating the same elements with both systems. If conflicts occur, use overwrite: 'auto' in GSAP to prevent overlap.

Use the browser DevTools console to log GSAP timelines and check selectors. For ScrollTrigger, enable markers: true to see trigger points. Always test on the published site, not the Designer, as Webflow Designer mode behaves differently.

Absolutely! Target CMS items using .w-dyn-items and .w-dyn-item classes. For dynamic loading, use MutationObserver to detect new items and animate them.

Yes! Use Webflow.push() with preventDefault() on links to create exit animations, then navigate to the new page. For advanced page transitions, integrate barba.js with GSAP for a seamless experience.

gsap.to() animates FROM current state TO the specified state. gsap.from() sets starting values and animates TO the current state. gsap.fromTo() lets you define both the start and end states explicitly.

Use Webflow Interactions for simple, visual hover/scroll effects you can build in the Designer. Use GSAP for complex animations, timelines, and code-based control. For anything beyond basic interactions, GSAP is the better choice.

GSAP is used by Disney, Google, and Apple. It's extremely optimized for performance. The key is implementing it correctly: avoid animating too many elements at once, use will-change, test on devices, and consider disabling animations on lower-powered devices.

Ready to Supercharge Your Webflow?

You now have everything you need to create stunning GSAP animations in Webflow. Start with ScrollTrigger basics and work your way up to advanced patterns.