Curved Carousel
An infinite scrolling carousel that appears curved
Installation
1
Install dependencies
npm install framer-motion tailwind-merge clsx
2
Add util file
lib/utils.ts
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
3
Add the following code in the tailwind.config.js file
import type { Config } from "tailwindcss";
export default {
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
animation: {
carousel: "carousel var(--duration) linear infinite",
},
keyframes: {
carousel: {
from: { transform: "translateX(0)" },
to: { transform: "translateX(calc(-100% - var(--gap)))" },
},
},
// other code
}
},
plugins: [require("tailwindcss-animate")],
} satisfies Config;
4
Copy the source code
components/ui/curved-carousel.tsx
'use client';
import Image from 'next/image';
import React from 'react';
type CurvedCarouselProps = {
imageSrcs: string[];
repeat?: number;
}
export function CurvedCarousel ({
imageSrcs,
repeat = 3
}: CurvedCarouselProps) {
const images = Array(repeat).fill(imageSrcs).flat();
return (
<div className="w-full h-full flex justify-center items-center bg-background">
<div className="relative w-full h-full overflow-hidden">
<div className="absolute z-10 left-1/2 w-[500%] h-[500%] bg-background rounded-[50%] -translate-x-1/2 -top-[490%]" />
<div className="absolute left-0 top-0 w-32 h-full z-20 backdrop-blur-[1px] dark:bg-gradient-to-r from-background to-transparent pointer-events-none" />
<div className="absolute right-0 top-0 w-32 h-full z-20 backdrop-blur-[1px] dark:bg-gradient-to-l from-background to-transparent pointer-events-none" />
<div className="flex [--duration:40s] [--gap:0px] [gap:var(--gap)]">
<div className="flex animate-carousel">
{images.map((src, index) => (
<div key={`slide-${index}`} className="relative flex-none w-1/3">
<div className="w-full h-full border-8 border-background">
<Image
src={src}
alt={`Slide ${index + 1}`}
width={400}
height={900}
className="w-full h-full object-cover"
/>
</div>
</div>
))}
</div>
</div>
<div className="absolute z-10 left-1/2 w-[500%] h-[500%] bg-background rounded-[50%] -translate-x-1/2 -bottom-[490%]" />
</div>
</div>
);
};
Props
Prop | Type | Description | Default Value |
---|---|---|---|
imageSrcs | string[] | The array of image sources that the carousel will display | undefined |
repeat | number | Number of times to repeat the array of images or elements for more seamless scrolling | 3 |
Inspired by kyu