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!

Remember you can read this tip online (with copy/pasteable code), and don’t forget to share VueDose with your colleagues, so they also know about these tips as well!

See you soon.

Alex.

Don't miss anything! Continue reading on VueDose

Start saving time and get a tip about the Vue ecosystem every week, right in your inbox.

    Latest Vue Jobs