Get Started
Get Started
Install the package, write the three elements the carousel measures and pick the styling flavour you already use.
Installation
Swipi ships as a single hook and nothing else — no components, no stylesheet to import, no peer plugins for autoplay or looping. React 18 and React 19 are both supported.
npm install @midstem/swipi-react
Frameworks
The engine is framework agnostic and each framework gets its own thin adapter around it. Every package below is published and bundles the engine, so you install one package and nothing else — and they all expose the same options and the same carousel object under their own idioms. Pick one here or in the selector next to the navigation, and the whole of this documentation follows it.
Your first carousel
Wiring is a single ref on the viewport. The track is its only child and the slides are the children of the track — everything else below is markup you own. Switch the tabs to read the same carousel as accessible or minimal markup, and with Tailwind classes or a plain stylesheet.
import { useSwipiCarousel } from '@midstem/swipi-react'
export const Carousel = ({ items }) => {
const [carouselRef, carousel] = useSwipiCarousel({ loop: true })
return (
<>
<div className="overflow-hidden touch-pan-y" ref={carouselRef}>
<div className="flex -ml-3 cursor-grab select-none active:cursor-grabbing">
{items.map((item) => (
<div className="min-w-0 shrink-0 grow-0 basis-1/2 pl-3" key={item.id}>
{item.title}
</div>
))}
</div>
</div>
</>
)
}The three elements
The hook measures the DOM rather than your props, so the geometry lives in three nested elements. Their class names are yours; only the declarations matter.
<div ref={carouselRef}> {/* viewport — overflow: hidden */}
<div> {/* track — display: flex */}
<div>Slide 1</div> {/* slides — flex: 0 0 … */}
<div>Slide 2</div>
</div>
</div>- Viewport — carries the ref and clips the track with
overflow: hidden. Pointer and drag listeners are attached to it directly, so nothing has to be spread onto your markup. - Track — the only child of the viewport, a
display: flexrow that the engine moves with a transform. - Slides — the children of the track. Their width decides how many are visible at once and where the carousel stops.
What you get back
The hook returns a tuple: the ref for the viewport and the carousel itself. The object is the whole API — state to render from and methods to command with, in one place. It keeps its identity between renders and is replaced only when something about it actually changes, so it is safe in a dependency array.
const [carouselRef, carousel] = useSwipiCarousel({ loop: true })
carousel.selectedIndex // 0
carousel.snapCount // 4
carousel.slidesCount // 5
carousel.canScrollNext // true
carousel.canScrollPrev // false
carousel.hasOverflow // true
carousel.scrollNext()
carousel.scrollPrev()
carousel.scrollTo(2)Where to go next
- Viewport & Track — the CSS contract behind the movement, written out in full.
- useSwipiCarousel — every option, its default and what it actually changes.
- Accessibility — roles, labels, keyboard support and reduced motion.
- Examples — the carousels from the front page, with the code that produced them.
