Skip to main content
Vuetify0 is now in alpha!
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

Carousel

Headless carousel built on CSS scroll-snap with multi-slide display and partial-slide peeking.


Renders elementIntermediateApr 15, 2026

Usage

The Carousel provides slide navigation with native drag/swipe via CSS scroll-snap. Slides register with a step context for programmatic navigation.

Slide 1
<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

vue
<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.

Slide 1

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.

Design
Develop
Test

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.

Mountains

Recipes

Circular Navigation

Enable wrapping from last slide to first with the circular prop.

vue
<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.

vue
<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.

vue
<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.

vue
<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:

css
/* 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↗.

ElementRole / Attribute
Rootrole="region", aria-roledescription="carousel", aria-label, aria-disabled
Viewportaria-live="polite"
Sliderole="group", aria-roledescription="slide", aria-label="N of M"
Previousaria-label via locale, aria-controls links to viewport
Nextaria-label via locale, aria-controls links to viewport
Indicatorrole="tablist" container with aria-orientation, role="tab" per dot, aria-selected
Progressrole="progressbar", aria-valuenow, aria-valuemin, aria-valuemax
LiveRegionrole="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.

API Reference

The following API details are for all variations of the Carousel component.

id

string | undefined

Unique identifier for the carousel

label

string | undefined

Accessible label for the carousel region

namespace

string | undefined

Namespace for dependency injection (must match child namespace)

Default: "v0:carousel"

disabled

boolean | undefined

Disables the entire carousel

Default: false

circular

boolean | undefined

Whether navigation wraps around from last to first

Default: false

orientation

CarouselOrientation | undefined

Carousel orientation

Default: "horizontal"

perView

number | undefined

Number of slides visible at once

Default: 1

autoplay

number | undefined

Autoplay interval in milliseconds. 0 disables autoplay.

Default: 0

behavior

ScrollBehavior | undefined

Scroll behavior for programmatic navigation

Default: "smooth"

modelValue

T | T[] | undefined

default

CarouselRootSlotProps

label

string | undefined

Accessible label for the indicator group

namespace

string | undefined

Namespace for connecting to parent Carousel.Root

Default: "v0:carousel"

default

CarouselIndicatorSlotProps

id

any

Unique identifier (auto-generated if not provided)

value

V | undefined

Value associated with this slide

disabled

MaybeRefOrGetter<boolean> | undefined

Disables this specific slide

namespace

string | undefined

Namespace for dependency injection

Default: "v0:carousel"

default

CarouselItemSlotProps

namespace

string | undefined

Namespace for connecting to parent Carousel.Root

Default: "v0:carousel"

default

CarouselLiveRegionSlotProps

namespace

string | undefined

Namespace for connecting to parent Carousel.Root

Default: "v0:carousel"

default

CarouselNextSlotProps

namespace

string | undefined

Namespace for connecting to parent Carousel.Root

Default: "v0:carousel"

default

CarouselPreviousSlotProps

namespace

string | undefined

Namespace for connecting to parent Carousel.Root

Default: "v0:carousel"

default

CarouselProgressSlotProps

namespace

string | undefined

Namespace for connecting to parent Carousel.Root

Default: "v0:carousel"

default

CarouselViewportSlotProps
Was this page helpful?

© 2016-1970 Vuetify, LLC
Ctrl+/