Today I will be showing you how to build a very simple image slider with React. By the time we are finished, we will be able to move back and forth with the use of left and right buttons.
To make things easy to follow, I am going to use create-react-app
and write all of the styles inside a CSS file.
- npx create-react-app image-slider
- cd image-slider
- npm i or yarn
Next, I am going to add Font Awesome by using a link tag provided by this cdn link
and paste it into the <head>
of public/index.html
. With that in place, that will now enable us to utilize icons for our left and right arrows.
I am going to change everything inside of src/App.js
, until I am left with the following:
import './App.css';
function App() {
return (
<div className="App">
<div className="slider">
<div className="slider-inner">{/* Slides go here. */}</div>
<div className="arrow left">
<i className="fa fa-chevron-left" />
</div>
<div className="arrow right">
<i className="fa fa-chevron-right" />
</div>
</div>
</div>
);
}
export default App;
And in src/App.css
, remove/add what you need until you are left with this stylesheet.
.slider {
position: relative;
height: 400px;
width: 600px;
margin: 0 auto;
overflow: hidden;
white-space: nowrap;
background: #ddd;
margin-top: 60px;
}
.slider-inner {
height: 100%;
}
.arrow {
display: flex;
position: absolute;
top: 50%;
height: 50px;
width: 50px;
justify-content: center;
background: white;
border-radius: 50%;
cursor: pointer;
align-items: center;
transition: transform ease-in 0.1s;
}
.arrow.left {
left: 25px;
}
.arrow.left > i {
transform: translateX(-2px);
}
.arrow.right {
right: 25px;
}
.arrow.right > i {
transform: translateX(2px);
}
.arrow.left:hover,
.arrow.right:hover {
transform: scale(1.1);
}
.slide {
height: 100%;
width: 600px;
display: inline-block;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
This will result in the following showing up on our page:
Now that we have something showing up on screen, I wanted to explain what are perhaps the most important divs of the slider. If you notice we have this hierarchy:
<div className="slider">
<div className="slider-inner">{/* Slides go here. */}</div>
...other code
</div>
The outer div .slider
, is set to an explicit height and width, and with an overflow of hidden
. The div .slider-inner
, is then
placed inside of it. Each slide will then be placed inside of .slider-inner
, where they
will sit side by side (due to the use of display: inline-block
). Seeing as each slide will be 100% width of .slider
, every
slide after the first one will overflow off screen to the right. In order to show different slides, all we need
to do is move .slider-inner
left or right by translating it on it's x axis.
Let's get started on displaying some slides first, and shelf the translate functionality for now.
I have four images I would like to display as slides, and am going to include them as an array directly in our
App.js
code. We can display them like so:
import './App.css';
const slides = [
'https://images.unsplash.com/photo-1449034446853-66c86144b0ad?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=80',
'https://images.unsplash.com/photo-1470341223622-1019832be824?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=80',
'https://images.unsplash.com/photo-1448630360428-65456885c650?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=80',
'https://images.unsplash.com/photo-1534161308652-fdfcf10f62c4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=80',
];
function App() {
return (
<div className="App">
<div className="slider">
<div className="slider-inner">
{slides.map((slide) => (
<div
key={slide}
className="slide"
style={{ backgroundImage: `url('${slide}')` }}
/>
))}
</div>
<div className="arrow left">
<i className="fa fa-chevron-left" />
</div>
<div className="arrow right">
<i className="fa fa-chevron-right" />
</div>
</div>
</div>
);
}
export default App;
After updating App.js
, we should now see this in the browser.
Now that we can visibly see the first slide, let's build the functionality to move to the right first. In order to do so,
let's wire up the right arrow to accept clicks from a user. We will add a new function called moveRight
to achieve this.
import { useState } from 'react';
import './App.css';
const slides = [
'https://images.unsplash.com/photo-1449034446853-66c86144b0ad?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=80',
'https://images.unsplash.com/photo-1470341223622-1019832be824?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=80',
'https://images.unsplash.com/photo-1448630360428-65456885c650?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=80',
'https://images.unsplash.com/photo-1534161308652-fdfcf10f62c4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=80',
];
function App() {
const [index, setIndex] = useState(0);
const [translate, setTranslate] = useState(0);
const moveRight = () => {
if (index === slides.length - 1) {
setIndex(0);
setTranslate(0);
} else {
setIndex((prev) => prev + 1);
setTranslate((prev) => prev - 600);
}
};
return (
<div className="App">
<div className="slider">
<div
className="slider-inner"
style={{
transform: `translateX(${translate}px)`,
}}
>
{slides.map((slide) => (
<div
key={slide}
className="slide"
style={{ backgroundImage: `url('${slide}')` }}
/>
))}
</div>
<div className="arrow left">
<i className="fa fa-chevron-left" />
</div>
<div className="arrow right" onClick={moveRight}>
<i className="fa fa-chevron-right" />
</div>
</div>
</div>
);
}
export default App;
So what's going on here? When the user clicks the right arrow, we check two scenarios:
.slider-inner
to the left 600px to show the next slide.Now that we can move right with our slider, we need to make sure that the user can also use the left arrow to go backwards. In similar fashion
to what we did to move right, let's create a new method called moveLeft
. To keep things simple, we are not going to allow the left arrow
to be clicked if we are on the first slide.
import { useState } from 'react';
import './App.css';
const slides = [
'https://images.unsplash.com/photo-1449034446853-66c86144b0ad?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=80',
'https://images.unsplash.com/photo-1470341223622-1019832be824?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=80',
'https://images.unsplash.com/photo-1448630360428-65456885c650?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=80',
'https://images.unsplash.com/photo-1534161308652-fdfcf10f62c4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=80',
];
function App() {
const [index, setIndex] = useState(0);
const [translate, setTranslate] = useState(0);
const moveRight = () => {
if (index === slides.length - 1) {
setIndex(0);
setTranslate(0);
} else {
setIndex((prev) => prev + 1);
setTranslate((prev) => prev - 600);
}
};
const moveLeft = () => {
if (index === 0) {
return;
} else {
setIndex((prev) => prev - 1);
setTranslate((prev) => prev + 600);
}
};
return (
<div className="App">
<div className="slider">
<div
className="slider-inner"
style={{
transform: `translateX(${translate}px)`,
}}
>
{slides.map((slide) => (
<div
key={slide}
className="slide"
style={{ backgroundImage: `url('${slide}')` }}
/>
))}
</div>
<div className="arrow left" onClick={moveLeft}>
<i className="fa fa-chevron-left" />
</div>
<div className="arrow right" onClick={moveRight}>
<i className="fa fa-chevron-right" />
</div>
</div>
</div>
);
}
export default App;
Now that we can move left and right freely, let's add one more thing, a transition effect that makes moving slide to slide seem more natural and fluid.
This is much easier than you would think. All we need to do is update .slider-inner
in our App.css file with the following:
.slider-inner {
height: 100%;
transition: transform 0.45s ease;
}
Adding the transition property will slightly delay the slides from moving, so they don't change immediately.
That's about it for our simple React image slider. As always, if you have any problems understanding any of this, feel free to shoot me a message, and I'll make sure it's crystal clear.
P.S. Here is a full working version on Github.
See if you can separate this slider into it's own reusable component that can be imported into any file. Also, see if you can make it mobile responsive.