Carousel
Headless carousel built on CSS scroll-snap with multi-slide display and partial-slide peeking.
Usage
The Carousel provides slide navigation with native drag/swipe via CSS scroll-snap. Slides register with a step context for programmatic navigation.
<script setup lang="ts">
import { Carousel } from '@vuetify/v0'
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js'
</script>
<template>
<div class="flex flex-col gap-4">
<Carousel.Root>
<Carousel.Viewport class="rounded-lg overflow-hidden gap-4 cursor-grab data-[dragging]:cursor-grabbing">
<Carousel.Item
v-for="i in 5"
:key="i"
class="flex items-center justify-center h-48 rounded-lg text-lg font-medium bg-surface-variant text-on-surface-variant w-full shrink-0"
:value="i"
>
Slide {{ i }}
</Carousel.Item>
</Carousel.Viewport>
<div class="flex items-center justify-center gap-2 mt-3">
<Carousel.Previous class="p-2 rounded-full border border-divider hover:bg-surface-variant disabled:opacity-40">
<svg class="size-5" viewBox="0 0 24 24"><path :d="mdiChevronLeft" fill="currentColor" /></svg>
</Carousel.Previous>
<Carousel.Next class="p-2 rounded-full border border-divider hover:bg-surface-variant disabled:opacity-40">
<svg class="size-5" viewBox="0 0 24 24"><path :d="mdiChevronRight" fill="currentColor" /></svg>
</Carousel.Next>
</div>
</Carousel.Root>
</div>
</template>
Anatomy
<script setup lang="ts">
import { Carousel } from '@vuetify/v0'
</script>
<template>
<Carousel.Root>
<Carousel.Viewport>
<Carousel.Item />
</Carousel.Viewport>
<Carousel.Indicator />
<Carousel.Previous />
<Carousel.Next />
<Carousel.Progress />
<Carousel.LiveRegion />
</Carousel.Root>
</template>Examples
Indicator
Dot indicators show which slide is active and allow direct navigation. The Carousel.Indicator exposes an items array via slot props — render each dot with v-bind="item.attrs" for built-in keyboard navigation and ARIA.
Dot Navigation
Indicators between Previous/Next buttons with roving tabindex and aria-controls linking each dot to its slide.
Multi-Slide Display
Show multiple slides at once with the per-view prop. This is useful for card grids, skill lists, or product carousels where users can browse items in groups.
Three Slides Per View
A circular carousel showing 3 slides at once with a 12px gap between them.
Peek
Add padding to the viewport to reveal a portion of adjacent slides, signaling to the user that more content is available and encouraging drag/swipe interaction.
Partial Slide Visibility
A carousel with 48px peek on each side showing portions of adjacent slides.
Recipes
Circular Navigation
Enable wrapping from last slide to first with the circular prop.
<template>
<Carousel.Root circular>
<Carousel.Viewport>
<Carousel.Item v-for="i in 5" :key="i" :value="i">
Slide {{ i }}
</Carousel.Item>
</Carousel.Viewport>
</Carousel.Root>
</template>Vertical Orientation
Set orientation="vertical" for a vertically scrolling carousel.
<template>
<Carousel.Root orientation="vertical">
<Carousel.Viewport style="height: 300px">
<Carousel.Item v-for="i in 5" :key="i" :value="i">
Slide {{ i }}
</Carousel.Item>
</Carousel.Viewport>
</Carousel.Root>
</template>Disabled State
Disable all navigation via the disabled prop on the root.
<template>
<Carousel.Root disabled>
<Carousel.Viewport>
<Carousel.Item v-for="i in 3" :key="i" :value="i">
Slide {{ i }}
</Carousel.Item>
</Carousel.Viewport>
<Carousel.Previous>Previous</Carousel.Previous>
<Carousel.Next>Next</Carousel.Next>
</Carousel.Root>
</template>Instant Scroll
Set behavior="instant" to disable smooth scrolling on programmatic navigation.
<template>
<Carousel.Root behavior="instant">
<Carousel.Viewport>
<Carousel.Item v-for="i in 5" :key="i" :value="i">
Slide {{ i }}
</Carousel.Item>
</Carousel.Viewport>
</Carousel.Root>
</template>Styling with Data Attributes
All component state is exposed via data-* attributes for CSS-only styling:
/* Style the selected slide */
[data-selected] { opacity: 1; }
/* Dim inactive slides */
[aria-hidden="true"] { opacity: 0.4; }
/* Hide nav buttons at boundaries */
[data-edge] { visibility: hidden; }
/* Drag interaction */
[data-dragging] { cursor: grabbing; user-select: none; }Accessibility
The Carousel implements the WAI-ARIA Carousel Pattern↗.
| Element | Role / Attribute |
|---|---|
| Root | role="region", aria-roledescription="carousel", aria-label, aria-disabled |
| Viewport | aria-live="polite" |
| Slide | role="group", aria-roledescription="slide", aria-label="N of M" |
| Previous | aria-label via locale, aria-controls links to viewport |
| Next | aria-label via locale, aria-controls links to viewport |
| Indicator | role="tablist" container with aria-orientation, role="tab" per dot, aria-selected |
| Progress | role="progressbar", aria-valuenow, aria-valuemin, aria-valuemax |
| LiveRegion | role="status", aria-live="polite", aria-atomic |
Slides outside the visible window are marked with aria-hidden="true" so screen readers only announce visible content. The LiveRegion provides a dedicated announcement channel for slide changes, using a 100ms delay for reliable screen reader detection.
The per-view prop controls how many slides are visible at once. The Viewport measures its own layout (padding, gaps) from the DOM and computes each slide’s width automatically. It uses CSS scroll-snap so the browser handles all scroll physics and snap behavior natively.
The Carousel is built on CSS scroll-snap for native drag/swipe support. For fade transitions, use the Step provider component with Presence for mount/unmount animations — it gives you the same navigation model (next/prev/circular) without scroll-based layout.
Use the autoplay prop with an interval in milliseconds. The root slot exposes isAutoplay, isPaused, remaining, play, stop, pause, and resume for controlling playback. Autoplay pauses automatically during touch and mouse drag interactions. Use Carousel.Progress to visualize the timer.
<Carousel.Root v-slot="{ isAutoplay, play, stop }" :autoplay="5000">
<Carousel.Viewport>
<!-- slides -->
</Carousel.Viewport>
<Carousel.Progress class="h-1 bg-surface-variant" />
</Carousel.Root>Carousel.Root
Props
namespace
string | undefinedNamespace for dependency injection (must match child namespace)
Default: "v0:carousel"
modelValue
T | T[] | undefinedSlots
default
CarouselRootSlotPropsCarousel.Indicator
Props
Slots
default
CarouselIndicatorSlotPropsCarousel.Item
Props
Slots
default
CarouselItemSlotPropsCarousel.LiveRegion
Props
Slots
default
CarouselLiveRegionSlotPropsCarousel.Next
Props
Slots
default
CarouselNextSlotPropsCarousel.Previous
Props
Slots
default
CarouselPreviousSlotPropsCarousel.Progress
Props
Slots
default
CarouselProgressSlotPropsCarousel.Viewport
Props
Slots
default
CarouselViewportSlotProps