Carousel
Overview
A carousel built on top of Embla Carousel that lets users browse a sequence of content (images, cards, banners, etc.) horizontally or vertically in a constrained space. SCarousel combines a CarouselRoot-family of Aria primitives (zero styles) with the carouselVariants style recipe (8 slots: root/content/container/item/control/navigation/previous/next, 6 sizes × 2 orientations).
Use it for image galleries, product/article carousels, banner rotators, or any “one viewport at a time, paged back and forth” content. Prefer list or layout for static grids and masonry; prefer tree/list with virtualizer for drag-sorting or long virtual lists instead of a carousel.
SCarousel aggregates the primitives through CarouselCompact and exposes a slides-driven API. For fully custom compositions, fall back to the Aria CarouselRoot/CarouselContent/CarouselContainer/CarouselItem/CarouselControl/CarouselNavigation/CarouselPrevious/CarouselNext primitives.
Usage
Features
- 🧩 Aria/styled split —
CarouselCompactaggregates the 8 primitives and exposes 7*Propschannels (content/container/item/control/navigation/previous/next);SCarouselonly injects styles and forwards slots/events - 🧭 Horizontal / vertical —
orientationswitches the slide axis, the arrow-key navigation axis and the layout direction - 🌐 Direction-aware —
diris forwarded to Embla and swaps theArrowLeft/ArrowRightkey semantics under RTL - ⌨️ Keyboard reachable — when the root region is focused, arrow keys page back and forth;
previous/nextauto-disabledat the boundaries - ♿ Accessible by default — root
role="region"+aria-roledescription="carousel", each sliderole="group"+aria-roledescription="slide"; button default content andaria-labelfallbacks are localized viauseLocaleMessages - 🎛️ Controlled / uncontrolled — pass Embla configuration directly through
options(loop,align,dragFree, …); the root slot exposesscrollTo/scrollNext/scrollPrev/selectedIndex/scrollSnaps/progress - 🎨 8-slot styling — root/content/container/item/control/navigation/previous/next with 6 sizes (xs–2xl) and a
floatNavfloating-navigation layout - 🧩 Fully customizable — per-slot
uioverrides, rootclassoverride,slides-driven data withitem/control/previous/nextslots replacing the defaults
Component family
SCarousel(styled) — the entry wrapper;carouselVariantsrecipe with dynamic slot forwarding anduseForwardListenersevent mergingCarouselRoot(Aria) — the state owner: initializes/destroys the Embla instance viauseEmblaCarousel, derivescanScrollNext/canScrollPrev/selectedIndex/scrollSnaps/progress; rendersrole="region",data-orientation, the localizedaria-labelfallback and arrow-key navigationCarouselContent(Aria) — the viewport; holds the Embla root element reference and generates theidshared with the nav buttonsCarouselContainer(Aria) — the sliding track; hosts everyCarouselItemCarouselItem(Aria) — a single slide;role="group"+aria-roledescription="slide"CarouselControl(Aria) — the controls container; hosts the navigation or custom controlsCarouselNavigation(Aria) — the container for the previous/next buttonsCarouselPrevious/CarouselNext(Aria) —Button-based prev/next buttons; default icon rendered viaIcon, default text visually hidden (VisuallyHidden) and localizedCarouselCompact(Aria) — the aggregated composite; iteratesslidesintoCarouselItem, exposingitem/control/previous/nextslots
Demos
Basic
Vertical
Multi
Snap
Progress
Dot
API
Aria Layer
10Carousel<T>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the root element. |
ui | Partial<CarouselUi> | — | Per-slot class overrides for the component. |
size | ThemeSize | — | The size of the carousel, which determines the spacing and sizing of its elements. |
floatNav | boolean | — | Whether to use floating navigation, which positions the navigation controls outside of the carousel content and allows them to float over the content.
This is useful for carousels with a lot of content or when you want to maximize the space available for the slides. |
slides | T[] | — | Slides to be rendered in the carousel. |
contentProps | CarouselContentProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
containerProps | CarouselContainerProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
itemProps | CarouselItemProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
controlProps | CarouselControlProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
navigationProps | CarouselNavigationProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
previousProps | CarouselPreviousProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
nextProps | CarouselNextProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
dir | Direction | — | Reading direction of the component. |
orientation | DataOrientation | — | Orientation of the component. |
options | Partial<import("../../../node_modules/embla-carousel/esm/components/Options").OptionsType> | — | Options. |
plugins | import("embla-carousel").CreatePluginType<import("../../../node_modules/embla-carousel/esm/components/Plugins").LoosePluginType, {}>[] | — | Plugins. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
init | EmblaCarouselType | Emitted when init occurs. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
item | CarouselRootSlotProps & { slide: T; index: number; selected: boolean; } | Default slot for the carousel component, which can be used to render custom content in the carousel. The slot props include all the necessary information and methods to control the carousel, such as the carousel instance, whether it can scroll next or prev, the selected index, scroll snaps, progress, and methods to scroll next, prev, or to a specific index. |
control | CarouselRootSlotProps | Control slot for the carousel component, which can be used to render custom controls for the carousel. The slot props include all the necessary information and methods to control the carousel, such as the carousel instance, whether it can scroll next or prev, the selected index, scroll snaps, progress, and methods to scroll next, prev, or to a specific index. |
previous | CarouselRootSlotProps | Navigation slot for the carousel component, which can be used to render custom navigation for the carousel. The slot props include all the necessary information and methods to control the carousel, such as the carousel instance, whether it can scroll next or prev, the selected index, scroll snaps, progress, and methods to scroll next, prev, or to a specific index. |
next | CarouselRootSlotProps | Next slot for the carousel component, which can be used to render custom next controls for the carousel. The slot props include all the necessary information and methods to control the carousel, such as the carousel instance, whether it can scroll next or prev, the selected index, scroll snaps, progress, and methods to scroll next, prev, or to a specific index. |
CarouselCompact<T>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
slides | T[] | — | Slides to be rendered in the carousel. |
contentProps | CarouselContentProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
containerProps | CarouselContainerProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
itemProps | CarouselItemProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
controlProps | CarouselControlProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
navigationProps | CarouselNavigationProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
previousProps | CarouselPreviousProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
nextProps | CarouselNextProps | — | Per-slot properties for the component, allowing you to pass props directly to the sub components without needing to use the `v-slot` API. This is useful for quickly customizing the sub components without needing to use the full flexibility of slots. |
dir | Direction | — | Reading direction of the component. |
orientation | DataOrientation | — | Orientation of the component. |
options | Partial<import("../../../node_modules/embla-carousel/esm/components/Options").OptionsType> | — | Options. |
plugins | import("embla-carousel").CreatePluginType<import("../../../node_modules/embla-carousel/esm/components/Plugins").LoosePluginType, {}>[] | — | Plugins. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
init | EmblaCarouselType | Emitted when init occurs. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
item | CarouselRootSlotProps & { slide: T; index: number; selected: boolean; } | Default slot for the carousel component, which can be used to render custom content in the carousel. The slot props include all the necessary information and methods to control the carousel, such as the carousel instance, whether it can scroll next or prev, the selected index, scroll snaps, progress, and methods to scroll next, prev, or to a specific index. |
control | CarouselRootSlotProps | Control slot for the carousel component, which can be used to render custom controls for the carousel. The slot props include all the necessary information and methods to control the carousel, such as the carousel instance, whether it can scroll next or prev, the selected index, scroll snaps, progress, and methods to scroll next, prev, or to a specific index. |
previous | CarouselRootSlotProps | Navigation slot for the carousel component, which can be used to render custom navigation for the carousel. The slot props include all the necessary information and methods to control the carousel, such as the carousel instance, whether it can scroll next or prev, the selected index, scroll snaps, progress, and methods to scroll next, prev, or to a specific index. |
next | CarouselRootSlotProps | Next slot for the carousel component, which can be used to render custom next controls for the carousel. The slot props include all the necessary information and methods to control the carousel, such as the carousel instance, whether it can scroll next or prev, the selected index, scroll snaps, progress, and methods to scroll next, prev, or to a specific index. |
CarouselContainer
CarouselContent
CarouselControl
CarouselItem
CarouselNavigation
CarouselNext
Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
CarouselPrevious
Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
CarouselRoot
Props
| Prop | Type | Default | Description |
|---|---|---|---|
dir | Direction | — | Reading direction of the component. |
orientation | DataOrientation | — | Orientation of the component. |
options | Partial<import("../../../node_modules/embla-carousel/esm/components/Options").OptionsType> | — | Options. |
plugins | import("embla-carousel").CreatePluginType<import("../../../node_modules/embla-carousel/esm/components/Plugins").LoosePluginType, {}>[] | — | Plugins. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
init | EmblaCarouselType | Emitted when init occurs. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | CarouselRootSlotProps | — |
Notes
Architecture and benchmark differences
CarouselRoot owns the full Embla instance lifecycle (init/re-init/destroy, select/reInit events syncing scroll state) while every primitive stays style-free and only the UI wrapper injects the carouselVariants classes. This mirrors shadcn/ui’s headless/styled split, unlike Ant Design, Element Plus, Mantine and Naive UI which ship a carousel as a single styled component with configuration props. VeanUI deliberately routes behaviors such as autoplay, loop and align through options into Embla’s plugin system instead of declaring them as top-level props, keeping the API lean and letting users plug in any Embla plugin. The prev/next buttons default to disabled at the boundaries, derived from canScrollNext/canScrollPrev rather than depending on the loop configuration.
| Capability | VeanUI | shadcn/ui | Ant Design Carousel | Element Plus Carousel | Mantine Carousel | Naive UI Carousel |
|---|---|---|---|---|---|---|
| Aria/styled split | ✅ | ✅ | — | — | — | — |
| Horizontal / vertical orientation | ✅ | — | ✅ | ✅ | ✅ | ✅ |
| Direction-aware (RTL) | ✅ | ✅ | ✅ | — | — | — |
| Arrow-key navigation | ✅ | — | ✅ | ✅ | — | — |
Localized button/region aria-label | ✅ | — | — | — | — | — |
autoplay / loop / alignment | options | options | props | props | props | props |
| Composite with per-part props | ✅ | ✅ | — | — | — | — |
— = unsupported or a different interaction model (AntD/Element Plus/Mantine/Naive UI are single-package config-driven carousels; shadcn/ui’s Carousel block is a copy-source headless composition but hardcodes the nav button text and lacks arrow-key navigation).
Cautions
SCarouseldepends on Embla’s layout engine; under SSR the first paint cannot measure real sizes, so scroll snapping andcanScrollNext/canScrollPrevare only correct after client mount. Do not rely on the server-rendered initialselectedIndex/progress.optionsandpluginsare reactive: changing themreInits the Embla instance. Passing a fresh object literal on every render triggers unnecessary rebuilds — keep a stableref/shallowRefreference.floatNav: true(the default) absolutely positions the prev/next buttons to the sides of the content; in a narrow container they may overflow the visible area. Usefloat-nav="false"or override the navigation positioning viaui.- The default prev/next icons are
lucide:arrow-left/lucide:arrow-rightrendered through the ConfigProvidericonRender; without one, theIconcomponent renders nothing and only theVisuallyHiddentext carries the accessible name. CarouselItemkeys are derived from item order (index), so reorderingslidesdoes not reuse component instances — keep the order stable when slide content is stateful.
FAQ
How do I enable autoplay?
Configure { loop: true } in options and pass Embla’s Autoplay plugin through plugins:
<SCarousel :slides="slides" :options="{ loop: true }" :plugins="[Autoplay()]" />How do I render custom dots?
Use the control slot and read scrollSnaps/selectedIndex from the slot props, jumping with scrollTo:
<template #control="{ scrollSnaps, selectedIndex, scrollTo }">
<div v-for="(_, index) in scrollSnaps" :key="index" @click="scrollTo(index)" />
</template>How do I show progress or page numbers?
The progress slot prop is a 0–100 scroll percentage (0 on the first screen) you can feed to SProgress; page numbers come from selectedIndex + 1 over scrollSnaps.length (see the 06-progress and 05-snap demos).
How do I show multiple slides at once?
Override the item slot’s basis via ui (e.g. basis-1/2 md:basis-1/3) and tune snapping with options.align/containScroll (see the 04-multi demo).
How do I localize the prev/next buttons and region name?
The button text and region aria-label fallback follow the ConfigProvider locale (carousel.previous / carousel.next / carousel.ariaLabel); override per instance with aria-label when needed.
How do I replace the default buttons?
Use the previous/next or control slots to render custom controls — the slot props include canScrollNext/canScrollPrev and scrollNext/scrollPrev.