How to build an Auto-Playing Slideshow with React

Final result
  1. The trick
  2. Functionality
  • slideshow
  • slideshowSlider
  • slide
function Slideshow() {
return (
<div className="slideshow">
<div className="slideshowSlider">
<div className="slide"></div>
</div>
</div>
);
}

Step 1: show colored slides

const colors = ["#0088FE", "#00C49F", "#FFBB28"];

function Slideshow() {
return (
<div className="slideshow">
<div className="slideshowSlider">
{colors.map((backgroundColor, index) => (
<div className="slide" key={index} style={{ backgroundColor }}/>
))}
</div>
</div>
);
}

Step 2: styling

/* Slideshow */

.slideshow {
margin: 0 auto;
overflow: hidden;
max-width: 500px;
}
/* Slideshow */

.slideshow {
margin: 0 auto;
overflow: hidden;
max-width: 500px;
}

.slide {
height: 400px;
width: 100%;
border-radius: 40px;
}
Slides are on top of each other
/* Slideshow */

.slideshow {
margin: 0 auto;
overflow: hidden;
max-width: 500px;
}

.slide {
display: inline-block;

height: 400px;
width: 100%;
border-radius: 40px;
}
Not much changed
/* Slideshow */

.slideshow {
margin: 0 auto;
overflow: hidden;
max-width: 500px;
}

.slideshowSlider {
white-space: nowrap;
}

.slide {
display: inline-block;

height: 400px;
width: 100%;
border-radius: 40px;
}
No more wrapping to the next line

Step 3: create the buttons

const colors = ["#0088FE", "#00C49F", "#FFBB28"];

function Slideshow() {
return (
<div className="slideshow">
<div className="slideshowSlider">
{colors.map((backgroundColor, index) => (
<div
className="slide"
key={index}
style={{ backgroundColor }}
></div>
))}
</div>

<div className="slideshowDots">
{colors.map((_, idx) => (
<div key={idx} className="slideshowDot"></div>
))}
</div>
</div>
);
}
/* Slideshow */

.slideshow {
margin: 0 auto;
overflow: hidden;
max-width: 500px;
}

.slideshowSlider {
white-space: nowrap;
}

.slide {
display: inline-block;

height: 400px;
width: 100%;
border-radius: 40px;
}

/* Buttons */

.slideshowDots {
text-align: center;
}

.slideshowDot {
display: inline-block;
height: 20px;
width: 20px;
border-radius: 50%;

cursor: pointer;
margin: 15px 7px 0px;

background-color: #c4c4c4;
}
Color container and buttons (dots) are ready
Logic behind the slideshow’s functionality
const colors = ["#0088FE", "#00C49F", "#FFBB28"];

function Slideshow() {
const [index, setIndex] = React.useState(0);

return (
<div className="slideshow">
<div
className="slideshowSlider"
style={{ transform: `translate3d(${-index * 100}%, 0, 0)` }}
>
{colors.map((backgroundColor, index) => (
<div
className="slide"
key={index}
style={{ backgroundColor }}
></div>
))}
</div>

<div className="slideshowDots">
{colors.map((_, idx) => (
<div key={idx} className="slideshowDot"></div>
))}
</div>
</div>
);
}
const colors = ["#0088FE", "#00C49F", "#FFBB28"];
const delay = 2500;

function Slideshow() {
const [index, setIndex] = React.useState(0);

React.useEffect(() => {
setTimeout(
() =>
setIndex((prevIndex) =>
prevIndex === colors.length - 1 ? 0 : prevIndex + 1
),
delay
);

return () => {};
}, [index]);

return (
<div className="slideshow">
<div
className="slideshowSlider"
style={{ transform: `translate3d(${-index * 100}%, 0, 0)` }}
>
{colors.map((backgroundColor, index) => (
<div
className="slide"
key={index}
style={{ backgroundColor }}
></div>
))}
</div>

<div className="slideshowDots">
{colors.map((_, idx) => (
<div key={idx} className="slideshowDot"></div>
))}
</div>
</div>
);
}
Slideshow is working
/* Slideshow */

.slideshow {
margin: 0 auto;
overflow: hidden;
max-width: 500px;
}

.slideshowSlider {
white-space: nowrap;
transition: ease 1000ms;
}

.slide {
display: inline-block;

height: 400px;
width: 100%;
border-radius: 40px;
}

/* Buttons */

.slideshowDots {
text-align: center;
}

.slideshowDot {
display: inline-block;
height: 20px;
width: 20px;
border-radius: 50%;

cursor: pointer;
margin: 15px 7px 0px;

background-color: #c4c4c4;
}
Slideshow animation is better with transition
const colors = ["#0088FE", "#00C49F", "#FFBB28"];
const delay = 2500;

function Slideshow() {
const [index, setIndex] = React.useState(0);

React.useEffect(() => {
setTimeout(
() =>
setIndex((prevIndex) =>
prevIndex === colors.length - 1 ? 0 : prevIndex + 1
),
delay
);

return () => {};
}, [index]);

return (
<div className="slideshow">
<div
className="slideshowSlider"
style={{ transform: `translate3d(${-index * 100}%, 0, 0)` }}
>
{colors.map((backgroundColor, index) => (
<div
className="slide"
key={index}
style={{ backgroundColor }}
></div>
))}
</div>

<div className="slideshowDots">
{colors.map((_, idx) => (
<div
key={idx}
className={`slideshowDot${index === idx ? " active" : ""}`}
></div>
))}
</div>
</div>
);
}
/* Slideshow */

.slideshow {
margin: 0 auto;
overflow: hidden;
max-width: 500px;
}

.slideshowSlider {
white-space: nowrap;
transition: ease 1000ms;
}

.slide {
display: inline-block;

height: 400px;
width: 100%;
border-radius: 40px;
}

/* Buttons */

.slideshowDots {
text-align: center;
}

.slideshowDot {
display: inline-block;
height: 20px;
width: 20px;
border-radius: 50%;

cursor: pointer;
margin: 15px 7px 0px;

background-color: #c4c4c4;
}

.slideshowDot.active {
background-color: #6a0dad;
}
Buttons do now reflect the changes
const colors = ["#0088FE", "#00C49F", "#FFBB28"];
const delay = 2500;

function Slideshow() {
const [index, setIndex] = React.useState(0);

React.useEffect(() => {
setTimeout(
() =>
setIndex((prevIndex) =>
prevIndex === colors.length - 1 ? 0 : prevIndex + 1
),
delay
);

return () => {};
}, [index]);

return (
<div className="slideshow">
<div
className="slideshowSlider"
style={{ transform: `translate3d(${-index * 100}%, 0, 0)` }}
>
{colors.map((backgroundColor, index) => (
<div
className="slide"
key={index}
style={{ backgroundColor }}
></div>
))}
</div>

<div className="slideshowDots">
{colors.map((_, idx) => (
<div
key={idx}
className={`slideshowDot${index === idx ? " active" : ""}`}
onClick={() => {
setIndex(idx);
}}
></div>
))}
</div>
</div>
);
}
The timer value is not cleared
const colors = ["#0088FE", "#00C49F", "#FFBB28"];
const delay = 2500;

function Slideshow() {
const [index, setIndex] = React.useState(0);
const timeoutRef = React.useRef(null);

React.useEffect(() => {
timeoutRef.current = setTimeout(
() =>
setIndex((prevIndex) =>
prevIndex === colors.length - 1 ? 0 : prevIndex + 1
),
delay
);

return () => {};
}, [index]);

return (
<div className="slideshow">
<div
className="slideshowSlider"
style={{ transform: `translate3d(${-index * 100}%, 0, 0)` }}
>
{colors.map((backgroundColor, index) => (
<div
className="slide"
key={index}
style={{ backgroundColor }}
></div>
))}
</div>

<div className="slideshowDots">
{colors.map((_, idx) => (
<div
key={idx}
className={`slideshowDot${index === idx ? " active" : ""}`}
onClick={() => {
setIndex(idx);
}}
></div>
))}
</div>
</div>
);
}
const colors = ["#0088FE", "#00C49F", "#FFBB28"];
const delay = 2500;

function Slideshow() {
const [index, setIndex] = React.useState(0);
const timeoutRef = React.useRef(null);

function resetTimeout() {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
}

React.useEffect(() => {
resetTimeout();
timeoutRef.current = setTimeout(
() =>
setIndex((prevIndex) =>
prevIndex === colors.length - 1 ? 0 : prevIndex + 1
),
delay
);

return () => {
resetTimeout();
};
}, [index]);

return (
<div className="slideshow">
<div
className="slideshowSlider"
style={{ transform: `translate3d(${-index * 100}%, 0, 0)` }}
>
{colors.map((backgroundColor, index) => (
<div
className="slide"
key={index}
style={{ backgroundColor }}
></div>
))}
</div>

<div className="slideshowDots">
{colors.map((_, idx) => (
<div
key={idx}
className={`slideshowDot${index === idx ? " active" : ""}`}
onClick={() => {
setIndex(idx);
}}
></div>
))}
</div>
</div>
);
}
The timer value is now cleared

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store