Skip to content
Destyler UI Destyler UI Destyler UI

Carousel

an accessible carousel component that leverages native CSS Scroll Snap for smooth, performant scrolling between slides.

Features

Install

Install the component from your command line.

Terminal window
      
        
npm install @destyler/{carousel,vue}
Terminal window
      
        
npm install @destyler/{carousel,react}
Terminal window
      
        
npm install @destyler/{carousel,svelte}
Terminal window
      
        
npm install @destyler/{carousel,solid}

Anatomy

Import all parts and piece them together.

<script setup lang="ts">
import * as carousel from '@destyler/carousel'
import { normalizeProps, useMachine } from '@destyler/vue'
import { computed, useId } from 'vue'
const [state, send] = useMachine(
carousel.machine({
id: useId(),
}))
const api = computed(() =>
carousel.connect(state.value, send, normalizeProps),
)
</script>
<template>
<div v-bind="api.getRootProps()">
<div v-bind="api.getItemGroupProps()">
<div v-bind="api.getItemProps({ index:0 })">
<img>
</div>
</div>
<div v-bind="api.getControlProps()">
<button v-bind="api.getPrevTriggerProps()"/>
<div v-bind="api.getIndicatorGroupProps()">
<button v-bind="api.getIndicatorProps({ index: 0 })" />
</div>
<button v-bind="api.getNextTriggerProps()"/>
</div>
</div>
</template>
import * as carousel from '@destyler/carousel'
import { normalizeProps, useMachine } from '@destyler/react'
import { useId } from 'react'
export default function Carousel() {
const [state, send] = useMachine(
carousel.machine({
id: useId(),
})
)
const api = carousel.connect(state, send, normalizeProps)
return (
<>
<div {...api.getRootProps()}>
<div {...api.getItemGroupProps()}>
<div {...api.getItemProps({ index: 0 })}>
<img />
</div>
</div>
<div {...api.getControlProps()}>
<button {...api.getPrevTriggerProps()} />
<div {...api.getIndicatorGroupProps()}>
<button {...api.getIndicatorProps({ index: 0 })}/>
</div>
<button {...api.getNextTriggerProps()}/>
</div>
</div>
</>
)
}
<script lang="ts">
import * as carousel from '@destyler/carousel'
import { normalizeProps, useMachine } from '@destyler/svelte'
const uid = $props.id();
const [state, send] = useMachine(
carousel.machine({
id: uid,
}),
)
const api = $derived(carousel.connect(state, send, normalizeProps))
</script>
<div {...api.getRootProps()}>
<div {...api.getItemGroupProps()}>
<div {...api.getItemProps({ index: 0 })}>
<img />
</div>
</div>
<div {...api.getControlProps()}>
<button {...api.getPrevTriggerProps()}>
</button>
<div {...api.getIndicatorGroupProps()}>
<button {...api.getIndicatorProps({ index: 0 })}></button>
</div>
<button {...api.getNextTriggerProps()}></button>
</div>
</div>
import type { Component } from 'solid-js'
import * as carousel from '@destyler/carousel'
import { normalizeProps, useMachine } from '@destyler/solid'
import { createMemo, createUniqueId } from 'solid-js'
const Carousel: Component = () => {
const [state, send] = useMachine(
carousel.machine({
id: createUniqueId(),
}))
const api = createMemo(() => carousel.connect(state, send, normalizeProps))
return (
<>
<div {...api().getRootProps()}>
<div {...api().getItemGroupProps()}>
<div {...api().getItemProps({ index: 0 })}>
<img />
</div>
</div>
<div {...api().getControlProps()}>
<button {...api().getPrevTriggerProps()} />
<div {...api().getIndicatorGroupProps()}>
<button {...api().getIndicatorProps({ index: 0 })}/>
</div>
<button {...api().getNextTriggerProps()} />
</div>
</div>
</>
)
}
export default Carousel

To create a vertical carousel, set the orientation property in the machine’s context to vertical.

const [state, send] = useMachine(
carousel.machine({
orientation: "vertical",
}),
)

Setting the initial slide

To set the initial slide of the carousel, pass the page property to the machine’s context.

The page corresponds to the scroll snap position index based on the layout. It does not necessarily correspond to the index of the slide in the carousel.

const [state, send] = useMachine(
carousel.machine({
page: 2,
}),
)

Setting the number of slides to show at a time

To customize number of slides to show at a time, set the slidesPerPage property in the machine’s context. The value must be an integer.

const [state, send] = useMachine(
carousel.machine({
slidesPerPage: 2,
}),
)

Setting the number of slides to move at a time

To customize number of slides to move at a time, set the slidesPerMove property in the machine’s context. The value must be an integer or auto.

const [state, send] = useMachine(
carousel.machine({
slidesPerMove: 2,
}),
)

To allow looping of slides, set the loop property in the machine’s context to true.

const [state, send] = useMachine(
carousel.machine({
loop: true,
}),
)

Setting the gap between slides

To customize spacing between slides, set the spacing property in the machine’s context to a valid CSS unit.

const [state, send] = useMachine(
carousel.machine({
spacing: "20px",
}),
)

Listening for page changes

When the carousel page changes, the onPageChange callback is invoked.

const [state, send] = useMachine(
carousel.machine({
onPageChange(details) {
// details => { page: number }
console.log("selected page:", details.page)
},
}),
)

To allow dragging the carousel with the mouse, set the allowMouseDrag property in the machine’s context to true.

const [state, send] = useMachine(
carousel.machine({
allowMouseDrag: true,
}),
)

To allow the carousel to autoplay, set the autoplay property in the machine’s context to true.

const [state, send] = useMachine(
carousel.machine({
autoplay: true,
}),
)

Alternatively, you can configure the autoplay interval by setting the delay property in the machine’s context.

const [state, send] = useMachine(
carousel.machine({
autoplay: { delay: 2000 },
}),
)

Styling guide

Earlier, we mentioned that each carousel part has a data-part attribute added to them to select and style them in the DOM.

[data-part="root"] {
/* styles for the root part */
}
[data-part="item-group"] {
/* styles for the item-group part */
}
[data-part="item"] {
/* styles for the root part */
}
[data-part="control"] {
/* styles for the control part */
}
[data-part="next-trigger"] {
/* styles for the next-trigger part */
}
[data-part="prev-trigger"] {
/* styles for the prev-trigger part */
}
[data-part="indicator-group"] {
/* styles for the indicator-group part */
}
[data-part="indicator"] {
/* styles for the indicator part */
}
[data-part="autoplay-trigger"] {
/* styles for the autoplay-trigger part */
}

Active state

When a carousel’s indicator is active, a data-current attribute is set on the indicator.

[data-part="indicator"][data-current] {
/* styles for the indicator's active state */
}

Methods and Properties

Machine Context

The carousel machine exposes the following context properties:

ids
Partial<{ root: string; item(index: number): string; itemGroup: string; nextTrigger: string; prevTrigger: string; indicatorGroup: string; indicator(index: number): string; }>
The ids of the elements in the carousel. Useful for composition.
translations
IntlTranslations
The localized messages to use.
slidesPerPage
number
The number of slides to show at a time.
slidesPerMove
number | "auto"
The number of slides to scroll at a time. When set to `auto`, the number of slides to scroll is determined by the `slidesPerPage` property.
autoplay
boolean | { delay: number; }
Whether to scroll automatically. The default delay is 4000ms.
allowMouseDrag
boolean
Whether to allow scrolling via dragging with mouse
loop
boolean
Whether the carousel should loop around.
page
number
The index of the active page.
spacing
string
The amount of space between items.
padding
string
Defines the extra space added around the scrollable area, enabling nearby items to remain partially in view.
onPageChange
(details: PageChangeDetails) => void
Function called when the page changes.
inViewThreshold
number | number[]
The threshold for determining if an item is in view.
snapType
"proximity" | "mandatory"
The snap type of the item.
slideCount
number
The total number of slides. Useful for SSR to render the initial ating the snap points.
onDragStatusChange
(details: DragStatusDetails) => void
Function called when the drag status changes.
onAutoplayStatusChange
(details: AutoplayStatusDetails) => void
Function called when the autoplay status changes.
dir
"ltr" | "rtl"
The document's text/writing direction.
id
string
The unique identifier of the machine.
getRootNode
() => ShadowRoot | Node | Document
A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
orientation
Orientation
The orientation of the element.

Machine API

The carousel api exposes the following methods:

page
number
The current index of the carousel
pageSnapPoints
number[]
The current snap points of the carousel
isPlaying
boolean
Whether the carousel is auto playing
isDragging
boolean
Whether the carousel is being dragged. This only works when `draggable` is true.
canScrollNext
boolean
Whether the carousel is can scroll to the next view
canScrollPrev
boolean
Whether the carousel is can scroll to the previous view
scrollToIndex
(index: number, instant?: boolean) => void
Function to scroll to a specific item index
scrollTo
(page: number, instant?: boolean) => void
Function to scroll to a specific page
scrollNext
(instant?: boolean) => void
Function to scroll to the next page
scrollPrev
(instant?: boolean) => void
Function to scroll to the previous page
getProgress
() => number
Returns the current scroll progress as a percentage
play
() => void
Function to start/resume autoplay
pause
() => void
Function to pause autoplay
isInView
(index: number) => boolean
Whether the item is in view
refresh
() => void
Function to re-compute the snap points and clamp the page

Data Attributes

Root

name
desc
data-scope
carousel
data-part
root
data-orientation
The orientation of the carousel

ItemGroup

name
desc
data-scope
carousel
data-part
item-group
data-orientation
The orientation of the item
data-dragging
Present when in the dragging state

Item

name
desc
data-scope
carousel
data-part
item
data-index
The index of the item
data-inview
Present when in viewport
data-orientation
The orientation of the item

Control

name
desc
data-scope
carousel
data-part
control
data-orientation
The orientation of the control

PrevTrigger

name
desc
data-scope
carousel
data-part
prev-trigger
data-orientation
The orientation of the prevtrigger

NextTrigger

name
desc
data-scope
carousel
data-part
next-trigger
data-orientation
The orientation of the nexttrigger

IndicatorGroup

name
desc
data-scope
carousel
data-part
indicator-group
data-orientation
The orientation of the indicatorgroup

Indicator

name
desc
data-scope
carousel
data-part
indicator
data-orientation
The orientation of the indicator
data-index
The index of the item
data-readonly
Present when read-only
data-current
Present when current

AutoplayTrigger

name
desc
data-scope
carousel
data-part
autoplay-trigger
data-orientation
The orientation of the autoplaytrigger
data-pressed
Present when pressed