A lightweight custom carousel component is useful in many frontend projects because it gives you full control over styling, behavior, animations, and responsiveness without relying on heavy third-party libraries.
This article walks through building a clean, reusable carousel in React using useState, dynamic styling, and CSS transitions.
We begin by importing our carousel component into App.jsx.
import Carousel from './components/Carousel/Carousel.jsx';
export default function App() {
return (
<>
<Carousel />
</>
);
}
Inside Carousel.jsx, we start by importing dependencies and assets.
import { useState } from 'react';
import './Carousel.css';
import image1 from '../../assets/img/01.jpg';
import image2 from '../../assets/img/02.jpg';
import image3 from '../../assets/img/03.jpg';
import image4 from '../../assets/img/04.jpg';
The carousel uses a single piece of state to track the active slide.
const [count, setCount] = useState(0);
The count variable represents the current slide index. If count is 0, the first image is shown. If it becomes 1, the second image is shown, and so on.
All images are grouped into an array so we can render them dynamically:
const images = [image1, image2, image3, image4];
Navigation is handled with two functions:
const incrementCount = () => {
if (count < images.length - 1) {
setCount(prev => prev + 1);
}
};
and
const decrementCount = () => {
if (count > 0) {
setCount(prev => prev - 1);
}
};
The slide movement is achieved using a translated flex container.
<ul
className="slides"
style={{
width: `${images.length * 100}%`,
transform: `translateX(-${count * (100 / images.length)}%)`,
}}
>
{images.map(img => (
<li
key={img}
style={{
width: `${100 / images.length}%`,
}}
>
<img src={img} width="800" />
</li>
))}
</ul>
The two navigation buttons are are present only if nedded.
{count > 0 && (
<button onClick={decrementCount}>Previous</button>
)}
{count < images.length - 1 && (
<button onClick={incrementCount}>Next</button>
)}
The CSS die the carousel component
.carousel {
max-width: 600px;
overflow: hidden;
position: relative;
}
.slides {
display: flex;
transition: 0.3s;
list-style: none;
padding: 0;
margin: 0;
}
.controls {
position: absolute;
top: 50%;
display: none;
}
.carousel:hover .controls {
display: block;
}
.slides li img {
width: 100%;
height: auto;
object-fit: cover;
}
The result is a clean, dependency-free carousel built with React state, flexbox layout, and simple transform-based animation.
You can see the full app in this Stackblitz.
Top comments (0)