Most carousels rely on heavy UI libraries, but if you're using React with Tailwind CSS, you can build a smooth, responsive carousel without adding extra dependencies.
In this post, I’ll show you how to create a clean, functional image carousel with:
- ✅ Tailwind CSS for layout and responsiveness
- ✅ React state to control active slide
- ✅ No external packages (no Swiper, no Splide, no UI kits)
Let’s go 👇
📸 Step 1: Basic Carousel Layout
We start by laying out slides in a horizontally scrollable container.
// Carousel.tsx
"use client";
import { useState } from "react";
import Image from "next/image";
const images = [
"/images/carousel/img1.jpg",
"/images/carousel/img2.jpg",
"/images/carousel/img3.jpg",
];
export default function Carousel() {
const [current, setCurrent] = useState(0);
const next = () => setCurrent((prev) => (prev + 1) % images.length);
const prev = () => setCurrent((prev) => (prev - 1 + images.length) % images.length);
return (
<div className="relative w-full max-w-4xl mx-auto overflow-hidden">
<div className="flex transition-transform duration-500"
style={{ transform: `translateX(-${current * 100}%)` }}>
{images.map((src, index) => (
<div className="min-w-full" key={index}>
<Image src={src} alt={`Slide ${index + 1}`} width={1200} height={600} className="object-cover w-full" />
</div>
))}
</div>
{/* Arrows */}
<button onClick={prev} className="absolute left-4 top-1/2 -translate-y-1/2 bg-black/50 text-white px-3 py-1 rounded">
‹
</button>
<button onClick={next} className="absolute right-4 top-1/2 -translate-y-1/2 bg-black/50 text-white px-3 py-1 rounded">
›
</button>
</div>
);
}
🧠 Step 2: Tailwind Styling Notes
You don’t need overflow-x-scroll or flex wrapping here — instead:
Use min-w-full to force each slide to take 100% width
Use flex on the container to lay slides side-by-side
Add transform to shift the view based on current index
📱 Step 3: Make It Responsive Tailwind makes responsiveness easy — your slides will automatically adjust to screen size.
To improve UX:
Add object-cover to images
Use aspect-[16/9] or fixed height for consistency
Wrap the carousel in max-w-[90%] for mobile spacing
🔄 Optional: Add Auto Slide
useEffect(() => {
const interval = setInterval(() => {
setCurrent((prev) => (prev + 1) % images.length);
}, 5000);
return () => clearInterval(interval);
}, []);
Use this if you want the carousel to autoplay every 5 seconds.
🧩 Bonus: Dot Navigation
<div className="flex justify-center gap-2 mt-4">
{images.map((_, i) => (
<button
key={i}
onClick={() => setCurrent(i)}
className={`w-3 h-3 rounded-full ${i === current ? "bg-black" : "bg-gray-400"}`}
/>
))}
</div>
🏁 Final Thoughts You don’t need a huge UI library for every feature. Building your own carousel gives you:
Full control over layout
Zero extra bundle size
Tailored design with Tailwind
🚀 Need a version with swipe support, touch gestures, or accessibility? Let me know and I’ll cover it in a follow-up post!
