Build A Simple Image Slider With React

05/11/22

Intro

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.

Getting Started With Our Image Slider

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.

The Basic Structure

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:

react slider

The Most Important Divs

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.

Some Basic Slides

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.

react slider 2

Viewing Other Slides

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:

  • If we are at the last slide, then reset back to the first slide.
  • Else, move .slider-inner to the left 600px to show the next slide.

Handling Left Clicks

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.

Smooth Transitions

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.

Summary Of Our React Slider

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.

Challenge To You

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.

Want To Level Up Your JavaScript Game?

Book a private session with me, and you will be writing slick JavaScript code in no time.