Vertical Scroll Snapping Carousel with Swiper

Let's explore how to build a vertical carousel in Webflow with Swiper.js. This slider will snap to the user's scroll and loop infinitely. We'll also have a look at how we can leverage the vast Events API with CSS transitions and GSAP to animate items as they slide into view.

Watch the Tutorial on YouTubeGet the Project Cloneable

Inside <head> tag

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/9.0.5/swiper-bundle.css" integrity="sha512-CTWIgc35lLPcCl1OP7MNcrrES+jyBBvMEz8Cqx/v0hifPNjIpPsd/jUYTJ/41CYCrQdfuw7LopKaqqjXVLqejg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/9.0.5/swiper-bundle.min.js" integrity="sha512-cEcJcdNCHLm3YSMAwsI/NeHFqfgNQvO0C27zkPuYZbYjhKlS9+kqO5hZ9YltQ4GaTDpePDQ2SrEk8gHUVaqxig==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.4/gsap.min.js"></script>

Inside </body> tag

<script>
window.Webflow ||= [];
window.Webflow.push(() => {
  // Select elements on page that we will need
  const slideNumber = document.querySelector(".swiper-slide-num");
  const headings = document.querySelectorAll(".heading");

  // Store speed in a variable since we'll want to use it for animations too.
  const speed = 800;

  // Define Swiper instance
  const swiper = new Swiper(".swiper", {
    // optional parameters
    direction: "vertical",
    loop: true,
    speed: speed,
    mousewheel: true,
    keyboard: {
      enabled: true
    },
    pagination: {
      el: ".swiper-pagination-custom",
      bulletClass: "swiper-bullet-custom",
      bulletActiveClass: "is-active",
      bulletElement: "button",
      clickable: true
    }
  });

  // event that fires when swiper.activeIndex changes
  swiper.on("activeIndexChange", () => {
    // be careful which index you use! We want realIndex in this case
    let activeIndex = swiper.activeIndex;
    let realIndex = swiper.realIndex;
    // change out slide number
    slideNumber.textContent = realIndex + 1;

    // animate the heading with CSS transitions.
    headings.forEach((heading) => heading.classList.remove("is-active"));
    headings[realIndex].classList.add("is-active");
  });

  // event that fires when swiper goes to next slide
  swiper.on("slideNextTransitionStart", () => {
    gsap
      .timeline({ defaults: { duration: speed / 2000 } })
      .to(".front", { translateY: "100%" })
      .set(".front", { translateY: "-100%" })
      .to(".front", { translateY: "0%" });
  });

  // event that fires when swiper goes to previous slide
  swiper.on("slidePrevTransitionStart", () => {
    gsap
      .timeline({ defaults: { duration: speed / 2000 } })
      .to(".front", { translateY: "-100%" })
      .set(".front", { translateY: "100%" })
      .to(".front", { translateY: "0%" });
  });
});

</script>