Building a Custom Image Slider Component in React

Introduction

In this article, I'm going to show how to create custom React components for an image slider that will fetch the data from the API and load it in our React application.

Requirement

  • Basic knowledge of React
  • Knowledge about useState & useEffect hooks
  • Visual Studio Code

Step 1. We need to create a new React app using the below command.

npx create-react-app image-slider

Open the folder in the VS Code and then open the command prompt to install the React icons page.

npm install react-icons

Step 2. Create a new folder named components and then a new file named ImageSlider.js and add the following code.

import { useEffect, useState } from "react";
import { BsArrowLeftCircleFill, BsArrowRightCircleFill } from "react-icons/bs";
import "./styles.css";

function ImageSlider({ url, page = 1, limit = 5 }) {
  const [photos, setPhotos] = useState([]);
  const [currentSlide, setCurrentSlide] = useState(0);
  const [loading, setLoading] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);

  async function fetchPhotos(getUrl) {
    try {
      setLoading(true);

      const response = await fetch(`${getUrl}?page=${page}&limit=${limit}`);
      const data = await response.json();

      if (data) {
        setPhotos(data);
        setLoading(false);
      }
    } catch (e) {
      setErrorMessage(e.message);
      setLoading(false);
    }
  }

  useEffect(() => {
    if (url !== "") {
      fetchPhotos(url);
    }
  }, [url]);

  function handlePrevious() {
    setCurrentSlide(currentSlide === 0 ? photos.length - 1 : currentSlide - 1);
  }

  function handleNext() {
    setCurrentSlide(currentSlide === photos.length - 1 ? 0 : currentSlide + 1);
  }

  console.log(photos);
  if (loading) {
    return <div>Loading...</div>;
  }

  if (errorMessage !== null) {
    return <div>Error Occured !{errorMessage}</div>;
  }
  return (
    <div className="container">
      <BsArrowLeftCircleFill  onClick={handlePrevious} className="arrow left-arrow" />
      {photos && photos.length
        ? photos.map((imageItem, index) => (
            <img
              src={imageItem.download_url}
              alt={imageItem.author}
              key={imageItem.id}
              className={
                currentSlide === index
                  ? "current-image"
                  : "current-image hide-current-image"
              }
            />
          ))
        : null}

      <BsArrowRightCircleFill  onClick={handleNext} className="arrow right-arrow" />
      <span className="circle-indicators">
        {photos && photos.length
          ? photos.map((_, index) => (
              <button
                key={index}
                className={
                  currentSlide === index
                    ? "current-indicator"
                    : "current-indicator inactive-indicator"
                }
                onClick={() => setCurrentSlide(index)}
              ></button>
            ))
          : null}
      </span>
    </div>
  );
}

export default ImageSlider;

Step 3. Then, create a Styles.css to add the styles for our image slider.

.container {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 600px;
  height: 450px;
}

.current-image {
  border-radius: 0.5rem;
  box-shadow: 0px 0px 7px #666;
  width: 100%;
  height: 100%;
}

.arrow {
  position: absolute;
  width: 2rem;
  height: 2rem;
  color: #fff;
  filter: drop-shadow(0px 0px 5px #555);
}

.left-arrow {
  left: 1rem;
}

.right-arrow {
  right: 1rem;
}

.circle-indicators {
  display: flex;
  position: absolute;
  bottom: 1rem;
}

.current-indicator {
  background-color: #ffffff;
  height: 15px;
  width: 15px;
  border-radius: 50%;
  border: none;
  outline: none;
  margin: 0 0.2rem;
  cursor: pointer;
}

.hide-current-image {
  display: none;
}

.inactive-indicator {
  background-color: gray;
}

Now, we need to use the ImageSlider Component in the App.js file by removing the default and using the below code.

import ImageSlider from "./components/ImageSlider";
function App() {
  return (
    <div className="App">
      <ImageSlider
        url="https://picsum.photos/v2/list"
        page={"2"}
        limit={"10"}
      />
    </div>
  );
}

export default App;

We are fetching the images from the API Endpoint https://picsum.photos/v2/list?page=2&limit=10 but we need to provide the page and limit parameters as per our requirement.

Then, you can run your application, and we can see the image slider locally in our React app.

React app

This is the explanation of the code that we used on the Image Slider component.

  • useEffect: Executes a side effect (fetching photos) after the component mounts or when the URL changes.
  • useState: Manages state variables for photos, current slide, loading, and error messages.
  • BsArrowLeftCircleFill and BsArrowRightCircleFill: Render left and right navigation arrows using Bootstrap icons.
  • fetchPhotos: Asynchronously fetches photo data from the API and updates the state.
  • handlePrevious: Moves to the previous slide, wrapping around to the last slide if on the first.
  • handleNext: Moves to the next slide, wrapping around to the first slide if on the last.
  • setPhotos: Updates the state with the fetched photos array.
  • setCurrentSlide: Updates the state with the index of the currently displayed slide.
  • setLoading: Manages the loading state while fetching photos.
  • setErrorMessage: Sets an error message if the API request fails.
  • <img>: Displays the current photo based on the active slide index.
  • <button>: Render's slide indicators and updates the current slide on click.
  • CSS classes (current-image, hide-current-image, etc.): Manage visibility and styling of images and indicators.