Carousel component of shadcn/ui collection uses Embla Carousel under the hood.
So luckily it has full access to the embla API. You can easily get the instance of it via setApi
prop:
const [cApi, setCApi] = useState<CarouselApi>()
return (
<Carousel setApi={setCApi}>...</Carousel>
)
With API you can keep track of the current slide index like this:
const [slideIndex, setSlideIndex] = useState(0)
useEffect(() => {
if (!cApi) return
setSlideIndex(cApi.selectedScrollSnap())
cApi.on('select', () => {
setSlideIndex(cApi.selectedScrollSnap())
})
}, [cApi])
But often you need not only the index but some information from the slide. For example, product id or any other current slide data.
To archive that let’s modify a bit the Carousel layout adding some data attributes:
<CarouselItem data-id={product.id}>...</CarouselItem>
Then we can use slideNodes method to get the slides containers and search for the current slide via its index:
const index = cApi.selectedScrollSnap()
const curProductId = Number(cApi.slideNodes()[index].dataset.id)
Autoplay plugin
You can control the autoplay feature with 2 methods via embla API:
cApi.plugins().autoplay?.play()
cApi.plugins().autoplay?.stop()
And it’s possibly a good thing to wrap these methods into the useCallback
hook.
If you need to have the information about the play status, you can subscribe to these events:
const [isPlaying, setIsPlaying] = useState(true)
cApi.on('autoplay:play', () => {
setIsPlaying(true)
})
cApi.on('autoplay:stop', () => {
setIsPlaying(false)
})
There is also isPlaying method in the API, but after some attempts to make it work consistently, I gave up. So my recommendation here is to have the state for play status and to update it with the help of autoplay:play
and autoplay:stop
events.
scrollPrev / scrollTo
shadcn/ui Carousel component already has the CarouselPrevious
and CarouselNext
built-in components.
But you can only use them inside the Carousel
component.
And if you need to control the slides outside this container (e.g. with buttons placed somewhere else), you can use embla API directly.
Let’s make a button for going to the next slide:
<Button onClick={scrollNext}>Button</Button>
Then we can use API and to call the scrollNext method in the useCallback
hook:
const scrollNext = useCallback(() => {
if (!cApi) return
cApi.scrollNext()
}, [cApi])
Additionally, it’s a good practice to disable a button if you can’t move the slides anymore:
<Button
onClick={scrollNext}
disabled={!cApi?.canScrollNext()}
>
Button
</Button>
Hope you’ll enjoy the Embla Carousel API as I did and have a nice day.