GSAP ScrollTrigger Animations for Bootstrap 5 Page Sections

GSAP ScrollTrigger Animations for Bootstrap 5 Page Sections

Key Takeaways

  • GSAP ScrollTrigger (version 3.x) integrates cleanly with Bootstrap 5’s grid and utility classes using standard data- attributes or direct JavaScript selectors.
  • Section reveal animations should be initialised after the DOM is fully parsed, and deferred behind an IntersectionObserver fallback where GSAP is not loaded.
  • Staggered child animations, parallax backgrounds, and scroll-linked progress bars are all achievable with fewer than 30 lines of JavaScript per section.
  • Reduced-motion preferences must be respected: wrap every GSAP timeline in a prefers-reduced-motion media query check.
  • The Canvas HTML Template ships with plugins.min.js and functions.bundle.js, which already bundle several scroll-reveal utilities you can replace or extend with native GSAP ScrollTrigger.

What GSAP ScrollTrigger Actually Does

GSAP (GreenSock Animation Platform) is a JavaScript animation library maintained by GreenSock. The ScrollTrigger plugin, introduced in GSAP 3.0 and now at 3.12.x, attaches any GSAP tween or timeline to a scroll position rather than a fixed timer. It replaces the older ScrollMagic library and the fragile window.scrollY polling patterns that still litter many codebases.

Five concepts you need before writing any code:

  • trigger: the element whose position on screen starts the animation.
  • start / end: scroll positions described as "top 80%" (element top reaches 80% of the viewport).
  • scrub: when set to true or a number, the animation is linked directly to scroll position rather than playing once.
  • toggleActions: controls what happens on enter, leave, enterBack, and leaveBack (e.g. "play none none reverse").
  • markers: debug overlays you add during development and remove before shipping.

GSAP ScrollTrigger does not conflict with Bootstrap 5’s CSS. The one overlap worth watching: animating elements inside Bootstrap sticky or fixed components. Because position: fixed elements sit outside the normal scroll container, ScrollTrigger’s default calculations will be wrong for them.

GSAP ScrollTrigger Animations for Bootstrap 5 Page Sections, abstract concept illustration

Loading GSAP in a Bootstrap 5 Project

GSAP 3 is available on npm and via CDN. For a static HTML template workflow, the CDN approach is the fastest path to a working proof of concept.

<!-- Place before closing </body> tag -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
<script>
  gsap.registerPlugin(ScrollTrigger);
</script>

For a bundled workflow using npm, import and register the plugin once in your entry file:

<!-- npm install gsap, then in your JS entry point -->
<!-- import { gsap } from "gsap"; -->
<!-- import { ScrollTrigger } from "gsap/ScrollTrigger"; -->
<!-- gsap.registerPlugin(ScrollTrigger); -->

If you are working inside Canvas and already loading plugins.min.js, add GSAP after that bundle to avoid conflicts with existing scroll utilities. Register ScrollTrigger before any call to gsap.to() or gsap.from() that uses a scrollTrigger property, or the plugin simply will not be found.

Building a Basic Section Reveal Animation

The most common pattern on a Bootstrap 5 marketing page is a section reveal animation: a full-width .section or .row fades and slides up as it enters the viewport. Here is a minimal, production-safe implementation.

<section id="features" class="py-5">
  <div class="container">
    <div class="row g-4 reveal-row">
      <div class="col-md-4 reveal-item">
        <div class="card h-100 p-4">Feature One</div>
      </div>
      <div class="col-md-4 reveal-item">
        <div class="card h-100 p-4">Feature Two</div>
      </div>
      <div class="col-md-4 reveal-item">
        <div class="card h-100 p-4">Feature Three</div>
      </div>
    </div>
  </div>
</section>
<script>
  const prefersReducedMotion = window.matchMedia(
    "(prefers-reduced-motion: reduce)"
  ).matches;

  if (!prefersReducedMotion) {
    gsap.registerPlugin(ScrollTrigger);

    document.querySelectorAll(".reveal-row").forEach(function (row) {
      var items = row.querySelectorAll(".reveal-item");

      gsap.from(items, {
        scrollTrigger: {
          trigger: row,
          start: "top 80%",
          toggleActions: "play none none none"
        },
        opacity: 0,
        y: 40,
        duration: 0.7,
        stagger: 0.15,
        ease: "power2.out"
      });
    });
  }
</script>

stagger: 0.15 means each card starts its animation 150 ms after the previous one. y: 40 sets the starting vertical offset in pixels. Both values are straightforward to tune per section.

scroll animation html, abstract technical diagram

Parallax Backgrounds and Scrub Effects

A scrub animation ties playback directly to how far the user has scrolled, rather than triggering once and playing through. This works well for hero background images and decorative elements. Use it sparingly: scrubbing every section is visually exhausting and harder on mid-range devices.

<section id="hero" class="position-relative overflow-hidden" style="min-height: 80vh;">
  <div
    id="hero-bg"
    class="position-absolute top-0 start-0 w-100 h-100"
    style="background-image: url('images/hero.jpg'); background-size: cover;"
  ></div>
  <div class="position-relative container py-6 text-white">
    <h1>Your Headline Here</h1>
  </div>
</section>
<script>
  if (!prefersReducedMotion) {
    gsap.to("#hero-bg", {
      yPercent: 30,
      ease: "none",
      scrollTrigger: {
        trigger: "#hero",
        start: "top top",
        end: "bottom top",
        scrub: true
      }
    });
  }
</script>

ease: "none" is intentional here. Because scrub: true makes position directly proportional to scroll progress, adding an easing function creates a perceptible lag that feels broken rather than smooth.

If you are building long, scroll-heavy pages, pair this with the techniques covered in our post on Bootstrap 5 Scrollspy for long one-page sites to keep section navigation accurate while animations are running.

Adding a Scroll-Linked Progress Bar

A reading progress bar pinned to the top of the page is one of the most practical uses of ScrollTrigger’s scrub feature. On long landing pages or editorial content, it gives users a clear sense of where they are.

<div
  id="scroll-progress"
  style="
    position: fixed;
    top: 0;
    left: 0;
    height: 3px;
    width: 0%;
    background-color: var(--cnvs-themecolor, #1abc9c);
    z-index: 9999;
  "
></div>
<script>
  gsap.to("#scroll-progress", {
    width: "100%",
    ease: "none",
    scrollTrigger: {
      trigger: "body",
      start: "top top",
      end: "bottom bottom",
      scrub: 0.3
    }
  });
</script>

var(--cnvs-themecolor, #1abc9c) pulls the Canvas CSS custom property when available and falls back to a hex value otherwise, keeping the progress bar on-brand without hardcoding a colour. For more on using CSS variables across a template, see our guide on theming one HTML template for multiple client brands with CSS variables.

Performance, Layout Shift, and When Not to Use GSAP ScrollTrigger

GSAP ScrollTrigger is not always the right tool. Consider these trade-offs before committing to it on a project.

  • Initial paint: animating opacity and transform on entry means elements start invisible. If JavaScript loads slowly or fails, visitors see blank sections. Mitigate this with a no-js class on <html> that keeps all elements visible when JS is absent.
  • Cumulative Layout Shift (CLS): only animate opacity and transform. Never animate height, width, margin, or padding on scroll, as those properties force reflow and will tank your Core Web Vitals score.
  • Mobile performance: scrub and parallax effects are CPU-intensive on mid-range Android devices. Consider disabling scrub effects below a breakpoint using ScrollTrigger.matchMedia() (or its GSAP 3.11 replacement, gsap.matchMedia()).
  • When to skip it: if you only need a simple fade-in with no timeline sequencing, a CSS @keyframes animation triggered by an IntersectionObserver is smaller, faster to implement, and carries zero JavaScript library overhead. Reserve GSAP ScrollTrigger for sequences, counters, scrub effects, and anything requiring synchronised multi-element timelines.

Keeping your JavaScript bundle lean matters, especially on template projects where dozens of plugins compete for bandwidth. The same discipline applies to CSS: see our post on removing unused CSS from Bootstrap 5 with PurgeCSS for a complementary approach to keeping overall page weight controlled.

Integrating with the Canvas HTML Template

Canvas ships with its own scroll animation data attributes (such as data-animate="fadeInUp") powered by the bundled functions.bundle.js. These are convenient for standard reveals but offer no timeline control or scrub support.

The recommended approach is additive: keep Canvas’s built-in animations for sections where a simple fade is sufficient, and layer GSAP ScrollTrigger only on sections that need custom sequencing. Do not initialise both systems on the same element. Competing opacity values will cause a visible flash.

To disable Canvas’s built-in animation on a specific element, remove data-animate from the markup and initialise your GSAP tween on that element instead. Canvas’s animation code checks for the attribute before running, so omitting it is sufficient.

Frequently Asked Questions

Yes. ScrollTrigger operates on DOM elements regardless of how they are laid out. You can use any Bootstrap 5 selector (.row .col-md-4 .container) as a trigger or animation target. The only caveat is elements inside Bootstrap’s sticky or fixed components, where the scroll container may differ from the document root. Use ScrollTrigger’s scroller option to specify an alternative scroll container when needed.

Wrap all GSAP initialisation inside a check: if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches). This ensures that users who have enabled reduced motion in their operating system settings receive a static, fully visible page with no animation. GSAP also provides a gsap.matchMedia() API in 3.11 and later that lets you define animation blocks conditionally, which is cleaner for large codebases.

toggleActions defines four discrete callbacks (play, pause, resume, reverse) that fire at specific scroll positions, so the animation plays at its own speed. scrub removes that independence and ties animation progress directly to scroll position. Use toggleActions for reveal animations and scrub for parallax or progress indicators where the visual should track the user’s scroll speed exactly.

It can, if implemented carelessly. The main risk is Cumulative Layout Shift caused by elements starting in a hidden or offset state before JavaScript initialises. To mitigate this, only animate opacity and transform, never dimensions or spacing. Also ensure GSAP is loaded non-blocking and that all animated elements have a visible fallback when JavaScript is disabled or slow to execute.

Yes, but not on the same element. Canvas’s scroll animation checks for a data-animate attribute and applies its own class-based reveal. If you want GSAP to control a specific element, remove the data-animate attribute from that element and initialise your GSAP tween directly. The two systems will coexist without conflict as long as they target different elements.

Looking for a production-ready Bootstrap 5 HTML template? Browse Canvas Template demos and find the perfect starting point for your next project.

If you’re building with the Canvas HTML Template and want to ship production-ready Bootstrap 5 layouts faster, try Canvas Builder free — the visual builder that exports clean Canvas-ready markup in minutes.

Skip the setup and build it free

Spin up a complete Bootstrap 5 site, blog included, with Canvas Builder. No coding, no cost.

Share:
Canvas Team
Canvas Team

Tutorials and tips for building beautiful Bootstrap 5 websites with the Canvas HTML Template and Canvas Builder.

More from the Canvas Blog