Components
Carousel
Carousel
Animated carousel with auto-play, navigation arrows, dot indicators, and smooth slide transitions.
Slide 1
First Slide
Installation
1
Install the packages
npm i motion clsx tailwind-merge2
Add util file
lib/util.ts
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
3
Copy and paste the following code into your project
carousel.tsx
"use client";
import { cn } from "@/lib/utils";
import { motion, AnimatePresence } from "motion/react";
import React, { useState, useEffect } from "react";
import { FiChevronLeft, FiChevronRight } from "react-icons/fi";
interface CarouselItem {
id: string;
content: React.ReactNode;
title?: string;
}
interface CarouselProps {
items?: CarouselItem[];
autoPlay?: boolean;
autoPlayInterval?: number;
showDots?: boolean;
showArrows?: boolean;
}
const Carousel: React.FC<CarouselProps> = ({
items = [
{ id: "1", content: <div className="bg-blue-500 h-48 rounded-lg flex items-center justify-center text-white text-xl">Slide 1</div>, title: "First Slide" },
{ id: "2", content: <div className="bg-green-500 h-48 rounded-lg flex items-center justify-center text-white text-xl">Slide 2</div>, title: "Second Slide" },
{ id: "3", content: <div className="bg-purple-500 h-48 rounded-lg flex items-center justify-center text-white text-xl">Slide 3</div>, title: "Third Slide" },
],
autoPlay = true,
autoPlayInterval = 3000,
showDots = true,
showArrows = true
}) => {
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
if (!autoPlay) return;
const interval = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % items.length);
}, autoPlayInterval);
return () => clearInterval(interval);
}, [autoPlay, autoPlayInterval, items.length]);
const goToSlide = (index: number) => {
setCurrentIndex(index);
};
const goToPrev = () => {
setCurrentIndex((prev) => (prev - 1 + items.length) % items.length);
};
const goToNext = () => {
setCurrentIndex((prev) => (prev + 1) % items.length);
};
return (
<div className="relative w-full max-w-2xl overflow-hidden rounded-lg">
{/* Carousel Container */}
<div className="relative h-64 overflow-hidden rounded-lg">
<AnimatePresence mode="wait">
<motion.div
key={currentIndex}
className="absolute inset-0"
initial={{ opacity: 0, x: 300 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -300 }}
transition={{ duration: 0.5, ease: "easeInOut" }}
>
{items[currentIndex].content}
</motion.div>
</AnimatePresence>
</div>
{/* Navigation Arrows */}
{showArrows && (
<>
<button
onClick={goToPrev}
className="absolute left-2 top-1/2 -translate-y-1/2 rounded-full bg-black/50 p-2 text-white hover:bg-black/70 transition-colors"
>
<FiChevronLeft className="h-5 w-5" />
</button>
<button
onClick={goToNext}
className="absolute right-2 top-1/2 -translate-y-1/2 rounded-full bg-black/50 p-2 text-white hover:bg-black/70 transition-colors"
>
<FiChevronRight className="h-5 w-5" />
</button>
</>
)}
{/* Dots Indicator */}
{showDots && (
<div className="absolute bottom-4 left-1/2 flex -translate-x-1/2 space-x-2">
{items.map((_, index) => (
<button
key={index}
onClick={() => goToSlide(index)}
className={cn(
"h-2 w-2 rounded-full transition-colors",
index === currentIndex ? "bg-white" : "bg-white/50"
)}
/>
))}
</div>
)}
{/* Title */}
{items[currentIndex].title && (
<div className="mt-4 text-center">
<h3 className="text-lg font-semibold text-white">
{items[currentIndex].title}
</h3>
</div>
)}
</div>
);
};
export default Carousel;"use client";
import { cn } from "@/lib/utils";
import { motion, AnimatePresence } from "motion/react";
import React, { useState, useEffect } from "react";
import { FiChevronLeft, FiChevronRight } from "react-icons/fi";
interface CarouselItem {
id: string;
content: React.ReactNode;
title?: string;
}
interface CarouselProps {
items?: CarouselItem[];
autoPlay?: boolean;
autoPlayInterval?: number;
showDots?: boolean;
showArrows?: boolean;
}
const Carousel: React.FC<CarouselProps> = ({
items = [
{ id: "1", content: <div className="bg-blue-500 h-48 rounded-lg flex items-center justify-center text-white text-xl">Slide 1</div>, title: "First Slide" },
{ id: "2", content: <div className="bg-green-500 h-48 rounded-lg flex items-center justify-center text-white text-xl">Slide 2</div>, title: "Second Slide" },
{ id: "3", content: <div className="bg-purple-500 h-48 rounded-lg flex items-center justify-center text-white text-xl">Slide 3</div>, title: "Third Slide" },
],
autoPlay = true,
autoPlayInterval = 3000,
showDots = true,
showArrows = true
}) => {
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
if (!autoPlay) return;
const interval = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % items.length);
}, autoPlayInterval);
return () => clearInterval(interval);
}, [autoPlay, autoPlayInterval, items.length]);
const goToSlide = (index: number) => {
setCurrentIndex(index);
};
const goToPrev = () => {
setCurrentIndex((prev) => (prev - 1 + items.length) % items.length);
};
const goToNext = () => {
setCurrentIndex((prev) => (prev + 1) % items.length);
};
return (
<div className="relative w-full max-w-2xl overflow-hidden rounded-lg">
{/* Carousel Container */}
<div className="relative h-64 overflow-hidden rounded-lg">
<AnimatePresence mode="wait">
<motion.div
key={currentIndex}
className="absolute inset-0"
initial={{ opacity: 0, x: 300 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -300 }}
transition={{ duration: 0.5, ease: "easeInOut" }}
>
{items[currentIndex].content}
</motion.div>
</AnimatePresence>
</div>
{/* Navigation Arrows */}
{showArrows && (
<>
<button
onClick={goToPrev}
className="absolute left-2 top-1/2 -translate-y-1/2 rounded-full bg-black/50 p-2 text-white hover:bg-black/70 transition-colors"
>
<FiChevronLeft className="h-5 w-5" />
</button>
<button
onClick={goToNext}
className="absolute right-2 top-1/2 -translate-y-1/2 rounded-full bg-black/50 p-2 text-white hover:bg-black/70 transition-colors"
>
<FiChevronRight className="h-5 w-5" />
</button>
</>
)}
{/* Dots Indicator */}
{showDots && (
<div className="absolute bottom-4 left-1/2 flex -translate-x-1/2 space-x-2">
{items.map((_, index) => (
<button
key={index}
onClick={() => goToSlide(index)}
className={cn(
"h-2 w-2 rounded-full transition-colors",
index === currentIndex ? "bg-white" : "bg-white/50"
)}
/>
))}
</div>
)}
{/* Title */}
{items[currentIndex].title && (
<div className="mt-4 text-center">
<h3 className="text-lg font-semibold text-white">
{items[currentIndex].title}
</h3>
</div>
)}
</div>
);
};
export default Carousel;4
Update the import paths to match your project setup
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| items | CarouselItem[] | [] | Array of carousel items with id, content, and optional title. |
| autoPlay | boolean | true | Enable automatic slide transitions. |
| autoPlayInterval | number | 3000 | Time interval for auto-play in milliseconds. |
| showDots | boolean | true | Show dot indicators for navigation. |
| showArrows | boolean | true | Show navigation arrows. |