Default
Horizontal carousel with previous/next controls and 5 slides.
<Carousel className="w-full max-w-sm">
<CarouselContent>
<CarouselItem>
<div>Slide 1</div>
</CarouselItem>
…
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>Vertical orientation
Set orientation="vertical" and constrain the height to scroll vertically.
<Carousel orientation="vertical">
<CarouselContent className="h-[200px]">
<CarouselItem>…</CarouselItem>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>Multiple slides per view
Use basis-1/3 on each CarouselItem to show three items at once.
<Carousel>
<CarouselContent>
<CarouselItem className="basis-1/3">…</CarouselItem>
<CarouselItem className="basis-1/3">…</CarouselItem>
…
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>Product showcase with pagination
Use the Carousel API from useCarousel() to build pagination dots, active-slide state, and custom controls. This mirrors the step-by-step configurator pattern: related content, a visible heading, multiple visible slides, rewind, and optional reveal spacing.
Payroll
Payroll readiness
98%Validate approvals, exceptions, and country rules before cutoff.
Recruiting
Hiring pipeline
42Track open roles, interview load, and pending scorecards by team.
Insights
People analytics
12kCompare retention, engagement, and manager actions in one view.
Growth
Learning progress
86%Surface mandatory learning, completions, and overdue courses.
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext,
useCarousel,
} from '@mihcm/ui/Carousel';
<Carousel opts={{ align: 'start', loop: true }} aria-label="Related workflow carousel">
<CarouselContent>
{workflowSlides.map((slide) => (
<CarouselItem key={slide.title} className="sm:basis-1/2 lg:basis-1/3">
<WorkflowCard {...slide} />
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
<CarouselPagination />
</Carousel>function CarouselPagination() {
const { api } = useCarousel();
const [selectedIndex, setSelectedIndex] = useState(0);
const [snapCount, setSnapCount] = useState(0);
useEffect(() => {
if (!api) return;
const update = () => {
setSelectedIndex(api.selectedScrollSnap());
setSnapCount(api.scrollSnapList().length);
};
update();
api.on('select', update);
api.on('reInit', update);
return () => {
api.off('select', update);
api.off('reInit', update);
};
}, [api]);
return (
<div className="mt-5 flex items-center justify-center gap-2">
{Array.from({ length: snapCount }, (_, index) => (
<button
key={index}
type="button"
aria-label={`Go to slide ${index + 1}`}
aria-current={index === selectedIndex ? 'true' : undefined}
onClick={() => api?.scrollTo(index)}
className={cn(
'h-2.5 rounded-full transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
index === selectedIndex ? 'w-6 bg-primary' : 'w-2.5 bg-muted-foreground/40',
)}
/>
))}
</div>
);
}