Carousel
rivelle/carouselFreeComponent
An accessible touch-friendly carousel powered by Embla.
Live preview
live · rendered by the registry
Rendered by rivelle on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://rivelle.dev/r/carousel.jsonSource
1"use client";23import * as React from "react";4import useEmblaCarousel, {5 type UseEmblaCarouselType,6} from "embla-carousel-react";7import { ArrowLeft, ArrowRight } from "lucide-react";89import { cn } from "@/lib/utils";10import { Button } from "@/components/ui/button";1112type CarouselApi = UseEmblaCarouselType[1];13type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;14type CarouselOptions = UseCarouselParameters[0];15type CarouselPlugin = UseCarouselParameters[1];1617type CarouselProps = {18 opts?: CarouselOptions;19 plugins?: CarouselPlugin;20 orientation?: "horizontal" | "vertical";21 setApi?: (api: CarouselApi) => void;22};2324type CarouselContextProps = CarouselProps & {25 carouselRef: ReturnType<typeof useEmblaCarousel>[0];26 api: ReturnType<typeof useEmblaCarousel>[1];27 scrollPrev: () => void;28 scrollNext: () => void;29 canScrollPrev: boolean;30 canScrollNext: boolean;31};3233const CarouselContext = React.createContext<CarouselContextProps | null>(null);3435function useCarousel() {36 const context = React.useContext(CarouselContext);37 if (!context)38 throw new Error("useCarousel must be used within a <Carousel />");39 return context;40}4142function Carousel({43 orientation = "horizontal",44 opts,45 setApi,46 plugins,47 className,48 children,49 ...props50}: React.ComponentProps<"div"> & CarouselProps) {51 const [carouselRef, api] = useEmblaCarousel(52 { ...opts, axis: orientation === "horizontal" ? "x" : "y" },53 plugins,54 );55 const [canScrollPrev, setCanScrollPrev] = React.useState(false);56 const [canScrollNext, setCanScrollNext] = React.useState(false);5758 const onSelect = React.useCallback((api: CarouselApi) => {59 if (!api) return;60 setCanScrollPrev(api.canScrollPrev());61 setCanScrollNext(api.canScrollNext());62 }, []);63 const scrollPrev = React.useCallback(() => api?.scrollPrev(), [api]);64 const scrollNext = React.useCallback(() => api?.scrollNext(), [api]);65 const handleKeyDown = React.useCallback(66 (event: React.KeyboardEvent<HTMLDivElement>) => {67 if (event.key === "ArrowLeft") {68 event.preventDefault();69 scrollPrev();70 } else if (event.key === "ArrowRight") {71 event.preventDefault();72 scrollNext();73 }74 },75 [scrollPrev, scrollNext],76 );7778 React.useEffect(() => {79 if (api && setApi) setApi(api);80 }, [api, setApi]);81 React.useEffect(() => {82 if (!api) return;83 onSelect(api);84 api.on("reInit", onSelect);85 api.on("select", onSelect);86 return () => {87// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from rivelle
All 83 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | rivelle | Components | Free | 2 deps | |
| Alertalert | rivelle | Components | Free | 1 dep | |
| Alert Dialogalert-dialog | rivelle | Components | Free | 1 dep | |
| Aspect Ratioaspect-ratio | rivelle | Components | Free | 1 dep | |
| Avataravatar | rivelle | Components | Free | 1 dep | |
| Badgebadge | rivelle | Components | Free | 2 deps | |
| Breadcrumbbreadcrumb | rivelle | Components | Free | 2 deps | |
| Buttonbutton | rivelle | Components | Free | 2 deps |
