Slides that shrink and wait behind the neighbours away from the center, dots that stretch from one to the next and a sliding counter. A drop-in replacement for embla-carousel-react, with the same API.
Installation
Install the packages
Add the shadcn/ui component
Animate it
The CLI finds your shadcn/ui components through components.json, swaps their primitive imports for Animate UI's and removes the CSS animation classes Motion replaces.
shadcn/ui's Carousel is built on Embla Carousel. Animate UI's carousel exports exactly the same useEmblaCarousel and types and adds the animation, set from opts. animate-ui animate carousel swaps the embla-carousel-react import for it. It is built on embla-carousel-react, so keep that dependency (shadcn add carousel installs it).
Usage
import {
CarouselCounter,
CarouselDots,
} from '@animate-ui/react/primitives/carousel';
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from '@/components/ui/carousel';
export function Gallery() {
return (
<Carousel opts={{ loop: true, align: 'center', scale: 0.8, visible: 3 }}>
<CarouselContent className="-ml-1">
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index} className="pl-1">
<img
src={`/photo-${index}.jpg`}
className="aspect-square rounded-xl object-cover"
/>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
<CarouselDots />
<CarouselCounter />
</Carousel>
);
}Everything else is shadcn/ui's and Embla's: parts, sizes, spacing, orientation, options, API, events and plugins. Without scale and visible, it lays out and moves exactly like shadcn/ui's.
scaleis the neighbours' scale, from 0 to 1: the slides shrink away from the center (1 or none leaves them as they are). Use it withalign: 'center', in a loop for slides on both sides (without one, addcontainScroll: falseso the first and last slides come to the center too). It scales each slide's content, its first child, so give eachCarouselItemone.visiblesizes the slides so that many show, the selected one in the middle, edge to edge withscale, keeping your spacing (pl-*on the items). With an even number, the slides at the edges show half. A loop needs a slide more than it shows on each side: at least 5 slides for 3 visible, 7 for 4 or 5. The slides are sized once the carousel mounts: on a server-rendered page, giveCarouselItema close basis to start from.- The edges clip what overflows them, so draw a slide's outline inside it (a border or an inset ring).
- The arrows sit along the slides, whatever you put under them, standing up too.
- The slides out of sight, behind the others or past the edges, are
inertandaria-hiddenuntil they show. CarouselDotsandCarouselCountercome from the package: place them anywhere insideCarousel. Style them with classes (the dots and pill takecurrentColor).CarouselCountertakes aformatfor its text.
Animation
- With
scale, the slides follow the scroll, frame by frame, while dragged or snapping: the selected one at its full size, each slide outscaletimes smaller (0.8, then 0.64) and drawn in to keep the gap between slides, out to the onesvisibleshows; the ones past them wait behind, a little smaller still, and slide out from behind as they come in. In a loop, each slide takes the nearest way around, so both sides stay filled. It works the same standing up (orientation="vertical", with a height onCarouselContent). Each slide sits above the ones farther from the center. CarouselDots: the current dot is a pill that follows the scroll, its front stretching to the next dot over the first half of the way and its back catching up over the second. In a loop, from the last slide to the first, it stretches back across all the dots the same way.CarouselCounter: the number slides to the next one (Sliding Number).- Under reduced motion, the slides keep their size and the counter's number changes at once.
API Reference
useEmblaCarousel takes Embla's options and plugins; shadcn/ui's Carousel passes its opts to it. Animate UI adds:
opts
| Prop | Type | Default |
|---|---|---|
scale? | number | - |
visible? | number | - |
CarouselDots
The dots, one per slide. Takes the props of a div.
CarouselCounter
| Prop | Type | Default |
|---|---|---|
format? | (current: React.ReactNode, count: number) => React.ReactNode | "Slide {current} of {count}" |