How to Create an Image Slideshow in JavaScript
Build an image slideshow that displays one image at a time with next/prev buttons that wrap around the set.
How a slideshow is structured
A slideshow keeps an array of image sources (or an array of DOM elements) and an index pointing at whichever one is currently visible. Clicking "next" or "prev" changes the index and re-renders; only the image at the current index is shown, everything else stays hidden.
The trickiest part is making the index wrap: going "next" from the last image should loop back to the first, and going "prev" from the first should loop back to the last. The modulo operator (`%`) handles the forward wrap, and adding the array length before taking the modulo handles the backward wrap without ever going negative.
Example: slideshow with wrapping next/prev
<!DOCTYPE html>
<html>
<head>
<style>
.slideshow {
position: relative;
width: 320px;
max-width: 100%;
margin: 0 auto;
}
.slideshow img {
width: 100%;
display: block;
border-radius: 8px;
}
.slideshow-controls {
position: absolute;
top: 50%;
left: 0;
right: 0;
display: flex;
justify-content: space-between;
transform: translateY(-50%);
}
.slideshow-controls button {
background: rgba(0, 0, 0, 0.5);
color: #fff;
border: none;
padding: 8px 12px;
cursor: pointer;
font-size: 1rem;
}
.slideshow-caption {
text-align: center;
margin-top: 8px;
font-size: 0.85rem;
color: #555;
}
</style>
</head>
<body>
<div class="slideshow">
<img id="slide-img" src="" alt="Slideshow image">
<div class="slideshow-controls">
<button id="prev-btn" aria-label="Previous image">❮</button>
<button id="next-btn" aria-label="Next image">❯</button>
</div>
</div>
<p class="slideshow-caption" id="slide-caption"></p>
<script>
var images = [
{ src: 'https://picsum.photos/id/1015/320/200', caption: '1 of 3' },
{ src: 'https://picsum.photos/id/1016/320/200', caption: '2 of 3' },
{ src: 'https://picsum.photos/id/1018/320/200', caption: '3 of 3' }
];
var currentIndex = 0;
var imgEl = document.getElementById('slide-img');
var captionEl = document.getElementById('slide-caption');
function render() {
imgEl.src = images[currentIndex].src;
captionEl.textContent = images[currentIndex].caption;
}
document.getElementById('next-btn').addEventListener('click', function () {
currentIndex = (currentIndex + 1) % images.length;
render();
});
document.getElementById('prev-btn').addEventListener('click', function () {
currentIndex = (currentIndex - 1 + images.length) % images.length;
render();
});
render(); // show the first slide immediately
</script>
</body>
</html>`(currentIndex + 1) % images.length` moves forward and snaps back to 0 once it passes the last index. `(currentIndex - 1 + images.length) % images.length` does the same going backward - the `+ images.length` guarantees the value stays positive before the modulo runs, since JavaScript's `%` on a negative number does not wrap the way you might expect.
- Store slide data in an array so the number of slides can grow without touching the click handlers.
- Keep exactly one 'currentIndex' variable as the single source of truth for which slide is visible.
- Call render() once on page load so the first slide appears before any button is clicked.
Adding dot indicators
Dot indicators give visitors two things next/prev buttons can't: a sense of how many slides exist, and a way to jump straight to any one of them. Build the dots by looping over the same `images` array and creating one button per slide; clicking a dot sets `currentIndex` directly instead of adding or subtracting from it.
Example: clickable dot indicators
<!DOCTYPE html>
<html>
<head>
<style>
.slideshow {
position: relative;
width: 320px;
max-width: 100%;
margin: 0 auto;
}
.slideshow img {
width: 100%;
display: block;
border-radius: 8px;
}
.slideshow-controls {
position: absolute;
top: 50%;
left: 0;
right: 0;
display: flex;
justify-content: space-between;
transform: translateY(-50%);
}
.slideshow-controls button {
background: rgba(0, 0, 0, 0.5);
color: #fff;
border: none;
padding: 8px 12px;
cursor: pointer;
font-size: 1rem;
}
.slideshow-caption {
text-align: center;
margin-top: 8px;
font-size: 0.85rem;
color: #555;
}
.slideshow-dots {
display: flex;
justify-content: center;
gap: 8px;
margin-top: 10px;
}
.slideshow-dots button {
width: 10px;
height: 10px;
border-radius: 50%;
border: none;
background: #cfd4db;
cursor: pointer;
padding: 0;
}
.slideshow-dots button.is-active {
background: #2563eb;
}
</style>
</head>
<body>
<div class="slideshow">
<img id="slide-img" src="" alt="Slideshow image">
<div class="slideshow-controls">
<button id="prev-btn" aria-label="Previous image">❮</button>
<button id="next-btn" aria-label="Next image">❯</button>
</div>
</div>
<p class="slideshow-caption" id="slide-caption"></p>
<div class="slideshow-dots" id="slide-dots"></div>
<script>
var images = [
{ src: 'https://picsum.photos/id/1015/320/200', caption: '1 of 3' },
{ src: 'https://picsum.photos/id/1016/320/200', caption: '2 of 3' },
{ src: 'https://picsum.photos/id/1018/320/200', caption: '3 of 3' }
];
var currentIndex = 0;
var imgEl = document.getElementById('slide-img');
var captionEl = document.getElementById('slide-caption');
var dotsContainer = document.getElementById('slide-dots');
function render() {
imgEl.src = images[currentIndex].src;
captionEl.textContent = images[currentIndex].caption;
updateDots();
}
document.getElementById('next-btn').addEventListener('click', function () {
currentIndex = (currentIndex + 1) % images.length;
render();
});
document.getElementById('prev-btn').addEventListener('click', function () {
currentIndex = (currentIndex - 1 + images.length) % images.length;
render();
});
images.forEach(function (_, i) {
var dot = document.createElement('button');
dot.setAttribute('aria-label', 'Go to slide ' + (i + 1));
dot.addEventListener('click', function () {
currentIndex = i; // jump straight to the clicked slide
render();
});
dotsContainer.appendChild(dot);
});
function updateDots() {
var dotButtons = dotsContainer.querySelectorAll('button');
dotButtons.forEach(function (dot, i) {
dot.classList.toggle('is-active', i === currentIndex);
});
}
render(); // show the first slide and sync the dots immediately
</script>
</body>
</html>Frequently Asked Questions
- How do I create an image slideshow in JavaScript?
- Hide every slide except one, track the current index, and change which slide is visible when the next or previous button is clicked. Wrap the index with a modulo so it loops back around at either end.
- How do I make a slideshow advance automatically?
- Call the next-slide function on a
setInterval. Clear the interval when the user interacts, then restart it, otherwise your timer and their clicks fight each other and slides skip unpredictably.