Guides
Arrows
Previous and next buttons driven by canScrollPrev, canScrollNext and hasOverflow.
The whole carousel
Markup
Styles
import { useSwipiCarousel } from '@midstem/swipi-react'
export const Carousel = ({ items }) => {
const [carouselRef, carousel] = useSwipiCarousel()
return (
<>
<div className="overflow-hidden touch-pan-y" ref={carouselRef}>
<div className="flex -ml-4 cursor-grab select-none active:cursor-grabbing">
{items.map((item) => (
<div className="min-w-0 shrink-0 grow-0 basis-1/2 pl-4" key={item.id}>
{item.title}
</div>
))}
</div>
</div>
<button
type="button"
className="z-10 bg-transparent border-none cursor-pointer disabled:opacity-[0.35] disabled:cursor-default"
onClick={carousel.scrollPrev}
disabled={!carousel.canScrollPrev}
>
‹
</button>
<button
type="button"
className="z-10 bg-transparent border-none cursor-pointer disabled:opacity-[0.35] disabled:cursor-default"
onClick={carousel.scrollNext}
disabled={!carousel.canScrollNext}
>
›
</button>
</>
)
}Hiding the arrows entirely
A disabled pair of arrows on a list that already fits is noise. hasOverflow is false when there is nothing to scroll — when three slides sit in a viewport that shows three — so the controls can disappear along with the interaction.
React
const [carouselRef, carousel] = useSwipiCarousel()
return (
<div className="carousel">
<div className="carousel__viewport" ref={carouselRef}>
{/* … */}
</div>
{carousel.hasOverflow && <Arrows carousel={carousel} />}
</div>
)With loop
An infinite carousel can always move in both directions, so as long as there is something to scroll both flags stay true and the buttons never disable. If you want the arrows to react to the position anyway, render them from selectedIndex and snapCount instead.
React
const [carouselRef, carousel] = useSwipiCarousel({ loop: true })
carousel.canScrollPrev // true, on every snap
carousel.canScrollNext // true, on every snap