Carousel

A carousel with motion and swipe built using Embla.

Source code

Click here to see the source code for this component on GitHub. Feel free to copy it and adjust it for your own use.

About

The carousel component is built using the Embla Carousel library.

Installation

npx ui-thing@latest add carousel

Usage

<template>
  <UiCarousel>
    <UiCarouselContent>
      <UiCarouselItem>...</UiCarouselItem>
      <UiCarouselItem>...</UiCarouselItem>
      <UiCarouselItem>...</UiCarouselItem>
    </UiCarouselContent>
    <UiCarouselPrevious />
    <UiCarouselNext />
  </UiCarousel>
</template>

Examples

Sizes

To set the size of the items, you can use the basis utility class on the <UiCarouselItem />.

1
2
3
4
5

Other Size Example

<template>
  <!-- 33% of the carousel width. -->
  <UiCarousel>
    <UiCarouselContent>
      <UiCarouselItem class="basis-1/3">...</UiCarouselItem>
      <UiCarouselItem class="basis-1/3">...</UiCarouselItem>
      <UiCarouselItem class="basis-1/3">...</UiCarouselItem>
    </UiCarouselContent>
  </UiCarousel>
</template>

Responsive

<template>
  <!-- 50% on small screens and 33% on larger screens. -->
  <UiCarousel>
    <UiCarouselContent>
      <UiCarouselItem class="md:basis-1/2 lg:basis-1/3">...</UiCarouselItem>
      <UiCarouselItem class="md:basis-1/2 lg:basis-1/3">...</UiCarouselItem>
      <UiCarouselItem class="md:basis-1/2 lg:basis-1/3">...</UiCarouselItem>
    </UiCarouselContent>
  </UiCarousel>
</template>

Spacing

To set the spacing between the items, we use a pl-[VALUE] utility on the <UiCarouselItem /> and a negative -ml-[VALUE] on the <UiCarouselContent />.

Why: I tried to use the gap property or a grid layout on the CarouselContent but it required a lot of math and mental effort to get the spacing right. I found pl-[VALUE] and -ml-[VALUE] utilities much easier to use.

You can always adjust this in your own project if you need to.

1
2
3
4
5

Other Spacing Example

<template>
  <UiCarousel>
    <UiCarouselContent class="-ml-4">
      <UiCarouselItem class="pl-4"> ... </UiCarouselItem>
      <UiCarouselItem class="pl-4"> ... </UiCarouselItem>
      <UiCarouselItem class="pl-4"> ... </UiCarouselItem>
    </UiCarouselContent>
  </UiCarousel>
</template>

Responsive

<template>
  <UiCarousel>
    <UiCarouselContent class="-ml-2 md:-ml-4">
      <UiCarouselItem class="pl-2 md:pl-4"> ... </UiCarouselItem>
      <UiCarouselItem class="pl-2 md:pl-4"> ... </UiCarouselItem>
      <UiCarouselItem class="pl-2 md:pl-4"> ... </UiCarouselItem>
    </UiCarouselContent>
  </UiCarousel>
</template>

Orientation

Use the orientation prop to set the orientation of the carousel.

1
2
3
4
5

Thumbnail

Options

You can pass options to the carousel using the opts prop. See the Embla Carousel docs for more information.

<template>
  <UiCarousel :opts="{ align: 'start', loop: true }">
    <UiCarouselContent>
      <UiCarouselItem>...</UiCarouselItem>
      <UiCarouselItem>...</UiCarouselItem>
      <UiCarouselItem>...</UiCarouselItem>
    </UiCarouselContent>
  </UiCarousel>
</template>

API

Method 1

Use the @init-api emit method on <UiCarousel /> component to set the instance of the API.

1
2
3
4
5
Slide 0 of 0

Method 2

You can access it through setting a template ref on the <UiCarousel /> component.

<template>
  <UiCarousel ref="carouselContainerRef"> ... </UiCarousel>
</template>

<script setup lang="ts">
  import { UiCarousel } from "#components";

  const carouselContainerRef = ref<InstanceType<typeof UiCarousel> | null>(null);

  function accessApi() {
    carouselContainerRef.value?.carouselApi.on("select", () => {});
  }
</script>

Events

You can listen to events using the API. To get the API instance use the @init-api emit method on the <UiCarousel /> component

<template>
  <UiCarousel @init-api="setApi"> ... </UiCarousel>
</template>

<script setup lang="ts">
  import type { CarouselApi } from "~/utils/useCarousel";

  const api = ref<CarouselApi>();
  function setApi(val: CarouselApi) {
    api.value = val;
  }
  const stop = watch(api, (api) => {
    if (!api) return;
    // Watch only once or use watchOnce() in @vueuse/core
    nextTick(() => stop());
    api.on("select", () => {
      // Do something on select.
    });
  });
</script>

See the Embla Carousel docs for more information on using events.

Slot Props

You can get the reactive slot props like carouselRef, canScrollNext..Prev, scrollNext..Prev using the v-slot directive in the <UiCarousel v-slot="slotProps" /> component to extend the functionality.

<template>
  <UiCarousel v-slot="{ canScrollNext, canScrollPrev }">
    ...
    <UiCarouselPrevious v-if="canScrollPrev" />
    <UiCarouselNext v-if="canScrollNext" />
  </UiCarousel>
</template>

Plugins

You can use the plugins prop to add plugins to the carousel.

npm i embla-carousel-autoplay
<template>
  <UiCarousel class="w-full max-w-xs" :plugins="[Autoplay({ delay: 2000 })]"> ... </UiCarousel>
</template>

<script setup lang="ts">
  import Autoplay from "embla-carousel-autoplay";
</script>
1
2
3
4
5

See the Embla Carousel docs for more information on using plugins.