The most modern Carousel component using CSS Scroll Snap and Vue.js

Last week I went to Codemotion Madrid with my beloved friend Laura Bonmati. It's one of the biggest conferences about coding in Spain.

One of our favourite talks was "The latest features in CSS" (in Spanish) by Sonia Ruiz, and this tip comes from some ideas I took from her talk, so let's give her a shoutout on twitter for the inspiration!

Among the features she showed, I was amazed by scroll-snap and how easy is to create a carousel by using it, so I thought... let's make it a component!

First, create a Carousel.vue component with the following structure:

<template>
  <div class="carousel-wrapper">
    <div class="carousel" :style="{ width, height }">
      <slot/>
    </div>
  </div>
</template>

<script>
export default {
  props: ["width", "height"]
};
</script>

<style scoped>
.carousel {
  display: flex;
  overflow: scroll;
  scroll-snap-type: x mandatory;
}

.carousel > * {
  flex: 1 0 100%;
  scroll-snap-align: start;
}
</style>

The important parts here are the use of scroll-snap-type. Using it we're forcing the scroll to snap to every item inside the .carousel element, in the x axis.

Then you need to make every item in the carousel to take the full space of the container. You do that using flex: 1 0 100% and defining the items alignment with scroll-snap-align: start.

Notice that I'm also passing by a width and height properties so you can easily set the carousel dimensions from outside the component.

By the way, if you're not sure about how <slot/> work, you can check the articles Using scoped slots in Vue.js and New v-slot directive in Vue.js 2.6.0 and know more about them (including scoped slots).

Just like that, you have a carousel component that works smoothly using the mouse scroll!

But hey... let's add some functionality right? What if you want to change the carousel slides manually?

As you might have thought, the carousel "sliding" works according to the scroll, so you must play with the scroll position.

In fact, each slide scroll position is exactly the width of the carousel multiplied per the slide position in the carousel.

Let's add a couple of buttons that allow us to go back and forth in the carousel, so we can navigate through every item:

<template>
  <div class="carousel-wrapper">
    <div class="carousel" :style="{ width, height }">
      <slot/>
    </div>
    <button @click="changeSlide(-1)">Prev Slide</button>
    <button @click="changeSlide(1)">Next Slide</button>
  </div>
</template>

<script>
export default {
  props: ["width", "height"],
  methods: {
    changeSlide(delta) {
      const carousel = this.$el.querySelector(".carousel");
      const width = carousel.offsetWidth;
      carousel.scrollTo(carousel.scrollLeft + width * delta, 0);
    }
  }
};
</script>

These buttons call the changeSlide method passing by the number of positions you want to navigate back or forth.

The method basically takes the carousel current width, and calculates where should scroll by checking the current scroll position and adding the amount of pixels where it should navigate. That calculation is defined by carousel.scrollLeft + width * delta.

If you try the component now, you'll see that it works as expected, but the scroll is not smooth. Instead, it changes the slide immediately.

Luckily, modern CSS comes to the rescue again!

You can use the property scroll-behavior: smooth on the carousel element and the scrolling will be smooth again!

.carousel {
  display: flex;
  overflow: scroll;
  scroll-behavior: smooth;
  scroll-snap-type: x mandatory;
}

Finally, the way to use the component is as simple as this:

<template>
  <Carousel width="400px" height="250px">
    <div class="blue">Blue</div>
    <div class="green">Green</div>
    <div class="red">Red</div>
  </Carousel>
</template>

Do you realize how easy was to build this component? Modern CSS is rad!

I'm pretty sure you're now thinking on more ways to improve it: change slides automatically every X seconds, loop scrolling, go to a specific slide... please, tell me what you come up with and show it to me! I'll be more than happy to see what you accomplish!

Of course, keep in mind that this component uses very new CSS properties, so it's only supported in the most modern browsers.

But hey, you must be building the Carousel of the future!

Do you want to see this carousel working? Go to this CodeSandbox!

Don't miss out anything about Vue, your favourite framework.

Subscribe to receive all the articles we publish in a concise format, perfect for busy devs.

Related Articles

Creating UI components based on a Design System in Vue.js

Don't you know the benefits of using a Design System? Not sure how to structure your Vue.js components in an app? Read the article and find it out.

Alex Jover Morales

Alex Jover Morales

Jul 21, 2020

How to structure a Vue.js app using Atomic Design and TailwindCSS

Unsure about how to organize your Vue.js components? Learn in this tutorial how to do it using Atomic Design methodology. Examples and demos included!

Alba Silvente

Alba Silvente

Jun 16, 2020

The most modern Pie Chart component using CSS Conic Gradient and Vue.js

Build a Pie Chart component using one of the modern CSS features Conic Gradient

Alex Jover Morales

Alex Jover Morales

Oct 21, 2019

Sponsors

VueDose is proudly supported by its sponsors. If you enjoy it, consider supporting it to ensure the project maintainability.

Silver
Learning Partner