Core UI

Page Sections

Coming soon ..

Templates

Coming soon ..

Carousel

A carousel using Framer Motion, featuring smooth animations and transitions for an engaging slide experience

BannerSliderSwiperFramer MotionTailwindReact
1 / 3

Installation

1
Install the following dependencies:
npm install framer-motion lucide-react
2
Make sure you have added `cn` utility
How to add cn utility
3
Copy and paste the following code into your project.
"use client";
import { useState } from "react";
import { motion, useMotionValue } from "framer-motion";
import { ArrowLeft, ArrowRight } from "lucide-react";
import { cn } from "@/lib/utils";
interface SwipperProps {
images: string[];
className?: string;
}
export const Swiper: React.FC<SwipperProps> = ({ images, className }) => {
const [imgIndex, setImgIndex] = useState(0);
const dragX = useMotionValue(0);
const onDragEnd = () => {
const x = dragX.get();
if (x <= -10 && imgIndex < images.length - 1) {
setImgIndex(imgIndex + 1);
} else if (x >= 10 && imgIndex > 0) {
setImgIndex(imgIndex - 1);
}
};
return (
<div
className={cn(
"group relative aspect-square h-full w-full overflow-hidden rounded-lg",
className
)}
>
<div className="pointer-events-none absolute top-1/2 z-10 flex w-full -translate-y-1/2 justify-between px-5">
<button
style={{ opacity: imgIndex === 0 ? 0 : 1 }}
className="pointer-events-auto h-fit w-fit rounded-full bg-white/80 p-2 opacity-0 transition-opacity duration-300 group-hover:opacity-100"
onClick={() => imgIndex > 0 && setImgIndex(imgIndex - 1)}
>
<ArrowLeft className="stroke-neutral-600" size={20} />
</button>
<button
style={{ opacity: imgIndex === images.length - 1 ? 0 : 1 }}
className="pointer-events-auto h-fit w-fit rounded-full bg-white/80 p-2 opacity-0 transition-opacity duration-300 group-hover:opacity-100"
onClick={() =>
imgIndex < images.length - 1 && setImgIndex(imgIndex + 1)
}
>
<ArrowRight className="stroke-neutral-600" size={20} />
</button>
</div>
<div className="pointer-events-none absolute bottom-2 z-10 flex w-full items-center justify-center">
<div className="flex items-center justify-center rounded-md bg-black/50 p-1 text-xs text-white opacity-0 transition-opacity duration-300 group-hover:opacity-100">
{imgIndex + 1} / {images.length}
</div>
</div>
<motion.div
drag="x"
dragConstraints={{ left: 0, right: 0 }}
dragMomentum={false}
style={{ x: dragX }}
animate={{ translateX: `-${imgIndex * 100}%` }}
onDragEnd={onDragEnd}
transition={{
damping: 18,
stiffness: 90,
type: "spring",
duration: 0.15,
}}
className="flex h-full cursor-grab items-center rounded-[inherit] active:cursor-grabbing"
>
{images.map((src, i) => (
<motion.div
key={i}
className="h-full w-full shrink-0 overflow-hidden bg-neutral-800 object-cover first:rounded-l-[inherit] last:rounded-r-[inherit]"
>
<img
src={src}
className="pointer-events-none h-full w-full object-cover"
/>
</motion.div>
))}
</motion.div>
</div>
);
};