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-partattribute 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
idto theuseMachinehook. 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.slidesPerViewnumber | "auto"The number of slides to show at a time.loopbooleanWhether the carousel should loop around.indexnumberThe current slide index.spacingstringThe amount of space between slides.onIndexChange(details: SlideChangeDetails) => voidFunction called when the slide changes.idsPartial<{ 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.idstringThe unique identifier of the machine.getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
Machine API
The carousel api exposes the following methods:
indexnumberThe current index of the carouselscrollProgressnumberThe current scroll progress of the carouselisAutoplaybooleanWhether the carousel is currently auto-playingcanScrollNextbooleanWhether the carousel is can scroll to the next slidecanScrollPrevbooleanWhether the carousel is can scroll to the previous slidescrollTo(index: number, jump?: boolean) => voidFunction to scroll to a specific slide indexscrollToNext() => voidFunction to scroll to the next slidescrollToPrevious() => voidFunction to scroll to the previous slidegetItemState(props: ItemProps) => ItemStateReturns the state of a specific slideplay() => voidFunction to start/resume autoplaypause() => voidFunction to pause autoplay
Edit this page on GitHub