0 Tk

Carousel

Display images or content in a scrollable area.

Usage

Drag with your mouse, scroll with your mouse wheel or use the navigation arrows to navigate.

Pass an array to the items prop and use the default slot to display the content of each item.

<script setup lang="ts">
const items = [
  'https://picsum.photos/600/800?random=1',
  'https://picsum.photos/600/800?random=2',
  'https://picsum.photos/600/800?random=3',
  'https://picsum.photos/600/800?random=4',
  'https://picsum.photos/600/800?random=5',
  'https://picsum.photos/600/800?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" :items="items">
    <img :src="item" width="300" height="400" draggable="false">
  </UCarousel>
</template>

Snap

The carousel will snap the item to the center (snap-center). You can use the snap utility classes to change this behavior using the ui prop:

Snap to start

<script setup lang="ts">
const items = [
  'https://picsum.photos/400/600?random=1',
  'https://picsum.photos/400/600?random=2',
  'https://picsum.photos/400/600?random=3',
  'https://picsum.photos/400/600?random=4',
  'https://picsum.photos/400/600?random=5',
  'https://picsum.photos/400/600?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'snap-start' }">
    <img :src="item" width="200" height="300" draggable="false">
  </UCarousel>
</template>

Snap to end

<script setup lang="ts">
const items = [
  'https://picsum.photos/400/600?random=1',
  'https://picsum.photos/400/600?random=2',
  'https://picsum.photos/400/600?random=3',
  'https://picsum.photos/400/600?random=4',
  'https://picsum.photos/400/600?random=5',
  'https://picsum.photos/400/600?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'snap-end' }">
    <img :src="item" width="200" height="300" draggable="false">
  </UCarousel>
</template>

Learn more about the scroll-snap-align property on the Tailwind CSS documentation.

Size

Each item will take its own size by default in the carousel. You can use the basis / width utility classes to change this behavior using the ui prop:

<script setup lang="ts">
const items = [
  'https://picsum.photos/600/600?random=1',
  'https://picsum.photos/600/600?random=2',
  'https://picsum.photos/600/600?random=3',
  'https://picsum.photos/600/600?random=4',
  'https://picsum.photos/600/600?random=5',
  'https://picsum.photos/600/600?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-full md:basis-1/2 lg:basis-1/3' }">
    <img :src="item" class="w-full" draggable="false">
  </UCarousel>
</template>

You can also set a width on the container to center the carousel:

<script setup lang="ts">
const items = [
  'https://picsum.photos/600/800?random=1',
  'https://picsum.photos/600/800?random=2',
  'https://picsum.photos/600/800?random=3',
  'https://picsum.photos/600/800?random=4',
  'https://picsum.photos/600/800?random=5',
  'https://picsum.photos/600/800?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-full' }" class="w-64 mx-auto rounded-lg overflow-hidden">
    <img :src="item" class="w-full" draggable="false">
  </UCarousel>
</template>

Learn more about the flex-basis property on the Tailwind CSS documentation.

Navigation

Arrows

Use the arrows prop to enable prev and next buttons, they will be automatically disabled when the carousel reaches the first or last item.

<script setup lang="ts">
const items = [
  'https://picsum.photos/1920/1080?random=1',
  'https://picsum.photos/1920/1080?random=2',
  'https://picsum.photos/1920/1080?random=3',
  'https://picsum.photos/1920/1080?random=4',
  'https://picsum.photos/1920/1080?random=5',
  'https://picsum.photos/1920/1080?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-full' }" class="rounded-lg overflow-hidden" arrows>
    <img :src="item" class="w-full" draggable="false">
  </UCarousel>
</template>

You can also customize the prev and next buttons using the prev-button and next-button props:

<script setup lang="ts">
const items = [
  'https://picsum.photos/600/800?random=1',
  'https://picsum.photos/600/800?random=2',
  'https://picsum.photos/600/800?random=3',
  'https://picsum.photos/600/800?random=4',
  'https://picsum.photos/600/800?random=5',
  'https://picsum.photos/600/800?random=6'
]
</script>

<template>
  <UCarousel
    v-slot="{ item }"
    :items="items"
    :ui="{
      item: 'basis-full',
      container: 'rounded-lg'
    }"
    :prev-button="{
      color: 'gray',
      icon: 'i-heroicons-arrow-left-20-solid',
      class: '-start-12'
    }"
    :next-button="{
      color: 'gray',
      icon: 'i-heroicons-arrow-right-20-solid',
      class: '-end-12'
    }"
    arrows
    class="w-64 mx-auto"
  >
    <img :src="item" class="w-full" draggable="false">
  </UCarousel>
</template>

In this example, we move the buttons outside of the carousel container. You can also change this globally in ui.carousel.default.prevButton and ui.carousel.default.nextButton.

Indicators

Use the indicators prop to display a list of buttons at the bottom of the carousel to navigate between items.

<script setup lang="ts">
const items = [
  'https://picsum.photos/1920/1080?random=1',
  'https://picsum.photos/1920/1080?random=2',
  'https://picsum.photos/1920/1080?random=3',
  'https://picsum.photos/1920/1080?random=4',
  'https://picsum.photos/1920/1080?random=5',
  'https://picsum.photos/1920/1080?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-full' }" class="rounded-lg overflow-hidden" indicators>
    <img :src="item" class="w-full" draggable="false">
  </UCarousel>
</template>

The number of indicators will be automatically generated based on the number of items:

<script setup lang="ts">
const items = [
  'https://picsum.photos/600/600?random=1',
  'https://picsum.photos/600/600?random=2',
  'https://picsum.photos/600/600?random=3',
  'https://picsum.photos/600/600?random=4',
  'https://picsum.photos/600/600?random=5',
  'https://picsum.photos/600/600?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-full md:basis-1/2 lg:basis-1/3' }" indicators class="rounded-lg overflow-hidden">
    <img :src="item" class="w-full" draggable="false">
  </UCarousel>
</template>

You can customize the position of the buttons through ui.indicators.wrapper.

Autoplay

You can easily implement an autoplay behavior using the exposed API through a template ref:

<script setup lang="ts">
const items = [
  'https://picsum.photos/1920/1080?random=1',
  'https://picsum.photos/1920/1080?random=2',
  'https://picsum.photos/1920/1080?random=3',
  'https://picsum.photos/1920/1080?random=4',
  'https://picsum.photos/1920/1080?random=5',
  'https://picsum.photos/1920/1080?random=6'
]

const carouselRef = ref()

onMounted(() => {
  setInterval(() => {
    if (!carouselRef.value) return

    if (carouselRef.value.page === carouselRef.value.pages) {
      return carouselRef.value.select(0)
    }

    carouselRef.value.next()
  }, 3000)
})
</script>

<template>
  <UCarousel
    ref="carouselRef"
    v-slot="{ item }"
    :items="items"
    :ui="{ item: 'basis-full' }"
    class="rounded-lg overflow-hidden"
    indicators
  >
    <img :src="item" class="w-full" draggable="false">
  </UCarousel>
</template>

Slots

default

You can put anything inside the default slot, not only images. You will have access to the item and index properties in the slot scope:

<script setup lang="ts">
const items = [{
  name: 'Sébastien Chopin',
  to: 'https://github.com/Atinux',
  avatar: { src: 'https://ipx.nuxt.com/f_auto,s_192x192/gh_avatar/atinux' }
}, {
  name: 'Pooya Parsa',
  to: 'https://github.com/pi0',
  avatar: { src: 'https://ipx.nuxt.com/f_auto,s_192x192/gh_avatar/pi0' }
}, {
  name: 'Daniel Roe',
  to: 'https://github.com/danielroe',
  avatar: { src: 'https://ipx.nuxt.com/f_auto,s_192x192/gh_avatar/danielroe' }
}, {
  name: 'Anthony Fu',
  to: 'https://github.com/antfu',
  avatar: { src: 'https://ipx.nuxt.com/f_auto,s_192x192/gh_avatar/antfu' }
}]
</script>

<template>
  <UCarousel v-slot="{ item, index }" :items="items" :ui="{ item: 'w-full' }">
    <div class="text-center mx-auto">
      <img :src="item.avatar.src" :alt="item.name" class="rounded-full w-48 h-48 mb-2" draggable="false">

      <p class="font-semibold">
        {{ index + 1 }}. {{ item.name }}
      </p>
    </div>
  </UCarousel>
</template>

prev / next

With the arrows prop enabled, use the #prev and #next slots to set the content of the previous and next buttons. You will have access to the disabled property and on-click method in the slot scope:

<script setup lang="ts">
const items = [
  'https://picsum.photos/600/800?random=1',
  'https://picsum.photos/600/800?random=2',
  'https://picsum.photos/600/800?random=3',
  'https://picsum.photos/600/800?random=4',
  'https://picsum.photos/600/800?random=5',
  'https://picsum.photos/600/800?random=6'
]
</script>

<template>
  <UCarousel
    :items="items"
    :ui="{
      item: 'basis-full',
      container: 'rounded-lg'
    }"
    arrows
    class="w-64 mx-auto"
  >
    <template #default="{ item }">
      <img :src="item" class="w-full" draggable="false">
    </template>

    <template #prev="{ onClick, disabled }">
      <button :disabled="disabled" @click="onClick">
        Prev
      </button>
    </template>

    <template #next="{ onClick, disabled }">
      <button :disabled="disabled" @click="onClick">
        Next
      </button>
    </template>
  </UCarousel>
</template>

indicator

With the indicators prop enabled, use the #indicator slot to set the content of the indicators. You will have access to the active, page properties and on-click method in the slot scope:

<script setup lang="ts">
const items = [
  'https://picsum.photos/600/800?random=1',
  'https://picsum.photos/600/800?random=2',
  'https://picsum.photos/600/800?random=3',
  'https://picsum.photos/600/800?random=4',
  'https://picsum.photos/600/800?random=5',
  'https://picsum.photos/600/800?random=6'
]
</script>

<template>
  <UCarousel
    :items="items"
    :ui="{
      item: 'basis-full',
      container: 'rounded-lg',
      indicators: {
        wrapper: 'relative bottom-0 mt-4'
      }
    }"
    indicators
    class="w-64 mx-auto"
  >
    <template #default="{ item }">
      <img :src="item" class="w-full" draggable="false">
    </template>

    <template #indicator="{ onClick, page, active }">
      <UButton :label="String(page)" :variant="active ? 'solid' : 'outline'" size="2xs" class="rounded-full min-w-6 justify-center" @click="onClick(page)" />
    </template>
  </UCarousel>
</template>

You can customize the position of the buttons through ui.indicators.wrapper.

Props

Prop Type Default Description
ui { wrapper?: string; container?: string; item?: string; arrows?: DeepPartial<{ wrapper: string; }, any>; indicators?: DeepPartial<{ wrapper: string; base: string; active: string; inactive: string; }, any>; default?: DeepPartial<...>; } & { ...; } & { ...; } undefined UI configuration for the carousel
items any[] [] Array of items to display in the carousel
dir "ltr" | "rtl" "ltr" Direction of the carousel
prevButton Button & { class?: string; } config.default.prevButton Configuration for the previous button
nextButton Button & { class?: string; } config.default.nextButton Configuration for the next button
arrows boolean false Enable/disable navigation arrows
indicators boolean false Enable/disable indicators

API

When accessing the component via a template ref, you can use the following:

Method/Property Type Description
page number The current page
pages number The total number of pages
select(page) function Go to a specific page
next() function Go to the next page
prev() function Go to the previous page

Config

{
  wrapper: 'relative',
  container: 'relative w-full flex overflow-x-auto snap-x snap-mandatory scroll-smooth',
  item: 'flex flex-none snap-center',
  arrows: {
    wrapper: 'flex items-center justify-between'
  },
  indicators: {
    wrapper: 'absolute flex items-center justify-center gap-3 bottom-4 inset-x-0',
    base: 'rounded-full h-3 w-3',
    active: 'bg-primary-500 dark:bg-primary-400',
    inactive: 'bg-gray-100 dark:bg-gray-800'
  },
  default: {
    prevButton: {
      color: 'black',
      class: 'rtl:[&_span:first-child]:rotate-180 absolute start-4 top-1/2 transform -translate-y-1/2 rounded-full',
      icon: 'i-heroicons-chevron-left-20-solid'
    },
    nextButton: {
      color: 'black',
      class: 'rtl:[&_span:last-child]:rotate-180 absolute end-4 top-1/2 transform -translate-y-1/2 rounded-full',
      icon: 'i-heroicons-chevron-right-20-solid'
    }
  }
}