Carousel
A slideshow component that cycles through elements.
Features
- Support for horizontal and vertical orientations.
- Supports alignment of slides (start, center or end alignment).
- Supports custom number of slides to show at a time.
- Supports looping of slides.
- Supports custom spacing between slides.
Installation
To use the carousel machine in your project, run the following command in your command line:
npm install @zag-js/carousel @zag-js/react # or yarn add @zag-js/carousel @zag-js/react
npm install @zag-js/carousel @zag-js/solid # or yarn add @zag-js/carousel @zag-js/solid
npm install @zag-js/carousel @zag-js/vue # or yarn add @zag-js/carousel @zag-js/vue
npm install @zag-js/carousel @zag-js/vue # or yarn add @zag-js/carousel @zag-js/vue
This command will install the framework agnostic carousel logic and the reactive utilities for your framework of choice.
Anatomy
To set up the carousel correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-part
attribute to help identify them in the DOM.
Usage
First, import the carousel package into your project
import * as carousel from "@zag-js/carousel"
The carousel package exports two key functions:
machine
— The state machine logic for the carousel widget.connect
— The function that translates the machine's state to JSX attributes and event handlers.
You'll also need to provide a unique
id
to theuseMachine
hook. This is used to ensure that every part has a unique identifier.
Next, import the required hooks and functions for your framework and use the carousel machine in your project 🔥
import * as carousel from "@zag-js/carousel" import { normalizeProps, useMachine } from "@zag-js/react" export function Carousel() { const [state, send] = useMachine(carousel.machine({ id: "1" })) const api = carousel.connect(state, send, normalizeProps) return ( <div {...api.rootProps}> <button {...api.prevTriggerProps}>Prev</button> <button {...api.nextTriggerProps}>Next</button> <div {...api.viewportProps}> <div {...api.itemGroupProps}> {items.map((image, index) => ( <div {...api.getItemProps({ index })} key={index}> <img src={image} alt="" style={{ height: "300px", width: "100%", objectFit: "cover" }} /> </div> ))} </div> </div> </div> ) } const items = [ "https://tinyurl.com/5b6ka8jd", "https://tinyurl.com/7rmccdn5", "https://tinyurl.com/59jxz9uu", ]
import * as carousel from "@zag-js/carousel" import { normalizeProps, useMachine } from "@zag-js/solid" import { createMemo, createUniqueId, For } from "solid-js" export function Carousel() { const [state, send] = useMachine(carousel.machine({ id: createUniqueId() })) const api = createMemo(() => carousel.connect(state, send, normalizeProps)) return ( <div {...api().rootProps}> <button {...api().prevTriggerProps}>Prev</button> <button {...api().nextTriggerProps}>Next</button> <div {...api().viewportProps}> <div {...api().itemGroupProps}> <For each={items}> {(image, index) => ( <div {...api().getItemProps({ index() })}> <img src={image} alt="" style={{ height: "300px", width: "100%", "object-fit": "cover" }} /> </div> )} </For> </div> </div> </div> ) } const items = [ "https://tinyurl.com/5b6ka8jd", "https://tinyurl.com/7rmccdn5", "https://tinyurl.com/59jxz9uu", ]
import * as carousel from "@zag-js/carousel" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed, defineComponent } from "vue" export const Carousel = defineComponent({ name: "Carousel", setup() { const [state, send] = useMachine(carousel.machine({ id: "1" })) const apiRef = computed(() => carousel.connect(state.value, send, normalizeProps), ) return () => { const api = apiRef.value return ( <div {...api.rootProps}> <button {...api.prevTriggerProps}>Prev</button> <button {...api.nextTriggerProps}>Next</button> <div {...api.viewportProps}> <div {...api.itemGroupProps}> {items.map((image, index) => ( <div {...api.getItemProps({ index })} key={index}> <img src={image} alt="" style={{ height: "300px", width: "100%", objectFit: "cover", }} /> </div> ))} </div> </div> </div> ) } }, }) const items = [ "https://tinyurl.com/5b6ka8jd", "https://tinyurl.com/7rmccdn5", "https://tinyurl.com/59jxz9uu", ]
<script setup> import * as carousel from "@zag-js/carousel"; import { normalizeProps, useMachine } from "@zag-js/vue"; import { computed } from "vue"; const items = [ "https://tinyurl.com/5b6ka8jd", "https://tinyurl.com/7rmccdn5", "https://tinyurl.com/59jxz9uu", ] const [state, send] = useMachine(carousel.machine({ id: "1" })); const api = computed(() => carousel.connect(state.value, send, normalizeProps) ); </script> <template> <div ref="ref" v-bind="api.rootProps"> <button v-bind="api.prevTriggerProps">Prev</button> <button v-bind="api.nextTriggerProps">Next</button> <div v-bind="api.viewportProps"> <div v-bind="api.itemGroupProps"> <div v-for="(item, index) in items" :key="index" v-bind="api.getItemProps({ index: index })"> <img :src="image" alt="" :style="{ height: '300px', width: '100%', objectFit: 'cover', }" /> </div> </div> </div> </div> </template>
Changing the orientation
By default, the carousel is assumed to be horizontal. To change the orientation
to vertical, set the orientation
property in the machine's context to
vertical
.
In this mode, the carousel will use the arrow up and down keys to increment/decrement its value.
Don't forget to change the styles of the vertical carousel by specifying its height.
const [state, send] = useMachine( carousel.machine({ orientation: "vertical", }), )
Setting the initial slide
To set the initial slide of the carousel, pass the index
property to the
machine's context.
const [state, send] = useMachine( carousel.machine({ index: 2, }), )
Setting the alignment of the slides
To set the alignment, set the align
property in the machine's context to the
start
, center
, or end
value.
const [state, send] = useMachine( carousel.machine({ align: "center", }), )
Setting the number of slides to show at a time
To customize number of slides to show, set the slidesPerView
property in the
machine's context to a number value or auto
string.
const [state, send] = useMachine( carousel.machine({ slidesPerView: 2, }), )
Setting the carousel should loop around
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 amount of space 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: "16px", }), )
Listening for changes
When the carousel slide changes, the onIndexChange
callback is invoked.
const [state, send] = useMachine( carousel.machine({ onIndexChange(details) { // details => { index: number } console.log("selected slide:", details.index) }, }), )
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="item"] { /* styles for the root part */ } [data-part="viewport"] { /* styles for the viewport part */ } [data-part="item-group"] { /* styles for the item-group part */ } [data-part="item"] { /* styles for the item 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 */ }
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
The carousel's api
exposes the following methods and properties:
Machine Context
The carousel machine exposes the following context properties:
orientation
"horizontal" | "vertical"
The orientation of the carousel.align
"start" | "center" | "end"
The alignment of the slides in the carousel.slidesPerView
number | "auto"
The number of slides to show at a time.loop
boolean
Whether the carousel should loop around.index
number
The current slide index.spacing
string
The amount of space between slides.onIndexChange
(details: SlideChangeDetails) => void
Function called when the slide changes.ids
Partial<{ root: string; viewport: string; slide(index: number): string; slideGroup: string; nextSlideTrigger: string; prevSlideTrigger: string; indicatorGroup: string; indicator(index: number): string; }>
The ids of the elements in the carousel. Useful for composition.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.
Machine API
The carousel api
exposes the following methods:
index
number
The current index of the carouselscrollProgress
number
The current scroll progress of the carouselisAutoplay
boolean
Whether the carousel is currently auto-playingcanScrollNext
boolean
Whether the carousel is can scroll to the next slidecanScrollPrev
boolean
Whether the carousel is can scroll to the previous slidescrollTo
(index: number, jump?: boolean) => void
Function to scroll to a specific slide indexscrollToNext
() => void
Function to scroll to the next slidescrollToPrevious
() => void
Function to scroll to the previous slidegetItemState
(props: ItemProps) => ItemState
Returns the state of a specific slideplay
() => void
Function to start/resume autoplaypause
() => void
Function to pause autoplay
Edit this page on GitHub