Carousel

A slider to display multiple items in a scrollable view.

1
2
3
4
5
carousel-demo.tsx
import { AspectRatio } from "@/components/ui/aspect-ratio"
import {
	Carousel,
	CarouselContent,
	CarouselItem,
	CarouselNavigation,
	CarouselNext,
	CarouselPrevious,
} from "@/components/ui/carousel"
 
const slides = [1, 2, 3, 4, 5]
 
export function CarouselDemo() {
	return (
		<div className="w-60 sm:w-80 lg:w-96">
			<Carousel>
				<CarouselContent>
					{slides.map((slide) => (
						<CarouselItem key={slide}>
							<AspectRatio
								ratio={16 / 9}
								className="rounded-lg border bg-background"
							>
								<div className="flex size-full items-center justify-center text-xl font-semibold text-foreground">
									{slide}
								</div>
							</AspectRatio>
						</CarouselItem>
					))}
				</CarouselContent>
				<CarouselNavigation>
					<CarouselPrevious />
					<CarouselNext />
				</CarouselNavigation>
			</Carousel>
		</div>
	)
}

Installation

npx shadcn@latest add https://9ui.dev/r/carousel

Usage

Imports
import {
	Carousel,
	CarouselContent,
	CarouselItem,
	CarouselNavigation,
	CarouselNext,
	CarouselPrevious,
} from "@/components/ui/carousel"
Anatomy
<Carousel>
	<CarouselContent>
		<CarouselItem />
	</CarouselContent>
	<CarouselNavigation>
		<CarouselPrevious />
		<CarouselNext />
	</CarouselNavigation>
</Carousel>

Examples

Vertical

1
2
3
4
5
carousel-vertical.tsx
import { AspectRatio } from "@/components/ui/aspect-ratio"
import {
	Carousel,
	CarouselContent,
	CarouselItem,
	CarouselNavigation,
	CarouselNext,
	CarouselPrevious,
} from "@/components/ui/carousel"
 
const slides = [1, 2, 3, 4, 5]
 
export function CarouselVertical() {
	return (
		<div className="w-60 sm:w-80 lg:w-96">
			<Carousel orientation="vertical" options={{ loop: true }}>
				<CarouselContent className="aspect-video h-[-webkit-fill-available] w-full p-1">
					{slides.map((slide) => (
						<CarouselItem key={slide} className="basis-full">
							<AspectRatio
								ratio={16 / 9}
								className="rounded-lg border bg-background"
							>
								<div className="flex size-full items-center justify-center font-medium text-foreground">
									{slide}
								</div>
							</AspectRatio>
						</CarouselItem>
					))}
				</CarouselContent>
				<CarouselNavigation className="bottom-0.5">
					<CarouselPrevious />
					<CarouselNext />
				</CarouselNavigation>
			</Carousel>
		</div>
	)
}

Multiple

1
2
3
4
5
carousel-multiple.tsx
import { AspectRatio } from "@/components/ui/aspect-ratio"
import {
	Carousel,
	CarouselContent,
	CarouselItem,
	CarouselNavigation,
	CarouselNext,
	CarouselPrevious,
} from "@/components/ui/carousel"
 
const slides = [1, 2, 3, 4, 5]
 
export function CarouselMultiple() {
	return (
		<div className="w-60 sm:w-80 lg:w-96">
			<Carousel>
				<CarouselContent>
					{slides.map((slide) => (
						<CarouselItem key={slide} className="basis-1/3">
							<AspectRatio
								ratio={16 / 9}
								className="rounded-lg border bg-background"
							>
								<div className="flex size-full items-center justify-center text-xl font-semibold text-foreground">
									{slide}
								</div>
							</AspectRatio>
						</CarouselItem>
					))}
				</CarouselContent>
				<CarouselNavigation>
					<CarouselPrevious />
					<CarouselNext />
				</CarouselNavigation>
			</Carousel>
		</div>
	)
}

Looped

1
2
3
4
5
carousel-looped.tsx
import { AspectRatio } from "@/components/ui/aspect-ratio"
import {
	Carousel,
	CarouselContent,
	CarouselItem,
	CarouselNavigation,
	CarouselNext,
	CarouselPrevious,
} from "@/components/ui/carousel"
 
const slides = [1, 2, 3, 4, 5]
 
export function CarouselLooped() {
	return (
		<div className="w-60 sm:w-80 lg:w-96">
			<Carousel options={{ loop: true }}>
				<CarouselContent>
					{slides.map((slide) => (
						<CarouselItem key={slide}>
							<AspectRatio
								ratio={16 / 9}
								className="rounded-lg border bg-background"
							>
								<div className="flex size-full items-center justify-center text-xl font-semibold text-foreground">
									{slide}
								</div>
							</AspectRatio>
						</CarouselItem>
					))}
				</CarouselContent>
				<CarouselNavigation>
					<CarouselPrevious />
					<CarouselNext />
				</CarouselNavigation>
			</Carousel>
		</div>
	)
}

Thumbnail

Carousel slide
Carousel slide
Carousel slide
Carousel slide
Carousel slide
carousel-thumbnail.tsx
import { useState } from "react"
import Image from "next/image"
 
import { AspectRatio } from "@/components/ui/aspect-ratio"
import {
	Carousel,
	CarouselApi,
	CarouselContent,
	CarouselItem,
} from "@/components/ui/carousel"
 
const slides = [
	"https://images.pexels.com/photos/1616403/pexels-photo-1616403.jpeg?auto=compress&cs=tinysrgb&w=450&h=800&dpr=2",
	"https://images.pexels.com/photos/1293120/pexels-photo-1293120.jpeg?auto=compress&cs=tinysrgb&w=450&h=800&dpr=2",
	"https://images.pexels.com/photos/1103970/pexels-photo-1103970.jpeg?auto=compress&cs=tinysrgb&w=450&h=800&dpr=2",
	"https://images.pexels.com/photos/2011824/pexels-photo-2011824.jpeg?auto=compress&cs=tinysrgb&w=450&h=800&dpr=2",
	"https://images.pexels.com/photos/2471235/pexels-photo-2471235.jpeg?auto=compress&cs=tinysrgb&w=450&h=800&dpr=2",
]
 
export function CarouselThumbnail() {
	const [api, setApi] = useState<CarouselApi>()
 
	return (
		<div className="w-60 sm:w-80 lg:w-96">
			<Carousel setApi={setApi}>
				<CarouselContent>
					{slides.map((slide) => (
						<CarouselItem key={slide}>
							<AspectRatio
								ratio={16 / 9}
								className="rounded-lg border bg-background"
							>
								<Image
									src={slide}
									alt="Carousel slide"
									fill
									className="rounded-lg object-cover"
								/>
							</AspectRatio>
						</CarouselItem>
					))}
				</CarouselContent>
				<div className="mt-2 flex items-center justify-center gap-2">
					{slides.map((slide, index) => (
						<button
							key={slide}
							className="relative size-10"
							onClick={() => api?.scrollTo(index)}
						>
							<Image
								src={slide}
								alt="Carousel slide"
								fill
								className="rounded-md object-cover opacity-80 transition-opacity duration-200 hover:opacity-100"
							/>
						</button>
					))}
				</div>
			</Carousel>
		</div>
	)
}