Create an autoslide carousel in react

Learn how to create an autoslide caraousel in React.

Slider or Carousel are list of items which keep changing on user interaction or automatically depending upon the configuration.

We are going to create a similar component in react which will auto slide as well as change on user actions.

For our development we are going to use few extra packages.

  • classnames: This helps us to use CSS classes as javascript object, we just need to name our CSS file as filename.module.css.
  • react-spring: Package which helps to do animation with react components.

We are using external package for animation because it really helps to animate the component when it is mounted or removed, which is much better for performance.

Following is the folder structure of our component.
React slider folder structure

Slider component in react.

Our slider component is going to be composed of two parts.
1. Slider area where slides will be sliding.
2. Navigation control to change the slides.

Let us begin our development by importing all the required packages at the top.

import React, { useState, useEffect, useRef } from "react";
import { useTransition, animated } from "@react-spring/web";

const Carousel = () => {
  //Other code will go here
}

export default Carousel;

We will be showing list of images in the slides, so lets create the state object where we will store these array of images and use this to manage the component.

    const list = [
      "https://cdn.pixabay.com/photo/2019/12/30/13/10/lost-places-4729640_1280.jpg",
      "https://cdn.pixabay.com/photo/2018/09/16/15/31/boy-3681679_1280.jpg",
      "https://cdn.pixabay.com/photo/2018/09/11/00/47/trunks-3668420_1280.jpg",
      "https://cdn.pixabay.com/photo/2018/11/14/12/13/young-3815082_1280.jpg",
      "https://cdn.pixabay.com/photo/2018/11/14/12/12/young-3815077_1280.jpg",
      "https://cdn.pixabay.com/photo/2018/09/15/11/19/male-3679138_1280.jpg",
      "https://cdn.pixabay.com/photo/2018/11/14/12/10/young-3815069_1280.jpg",
      "https://cdn.pixabay.com/photo/2018/11/16/00/20/young-3818476_1280.jpg"
    ];

We will maintain two states, current will be used to track the active slide and next will be used to track the direction of the slide.

lets create the layout of the component. We will be wrapping our slide inside the two component that we have imported from the react-string package.

Our list of slides need to be placed inside the useTransition() hook to make the animation work.

animated.div makes the current component to animate on mounting and leaving.

You can read more about this on the react-string official site.

// Generate slides
  const animationDirection = next
    ? {
        from: { opacity: 0, transform: "translate3d(100%,0,0)" },
        enter: { opacity: 1, transform: "translate3d(0%,0,0)" },
        leave: { opacity: 0, transform: "translate3d(-50%,0,0)" }
      }
    : {
        from: { opacity: 0, transform: "translate3d(-50%,0,0)" },
        enter: { opacity: 1, transform: "translate3d(0%,0,0)" },
        leave: { opacity: 0, transform: "translate3d(100%,0,0)" }
      };

  const transitions = useTransition(current, {
    key: current,
    from: animationDirection.from,
    enter: animationDirection.enter,
    leave: animationDirection.leave,
  });

  const item = transitions((style, i) => (
    <animated.div
      className={"slide"}
      style={{
        ...style,
        backgroundImage: `url(${list[i]}`,
      }}
    />
  ));

  return (
    <>
      {/* Slides */}
      <div className={"wrapper"}>{item}</div> 

      {/* Controls */}
      <div className={"controls"}>
        <span onClick={handlePrev}>Prev</span>
        <span onClick={handleNext}>Next</span>
      </div>
    </>
  );

We have generated the list of slides and wrapped the content inside the animate.div.

As transition passes props to its children, that is why we are returning function in the slides list and passing that props as style object.

On Transition of the items we are passing the current because that is returned in the callback and we will be using this to fetch the slide at the current index with {list[i]}.

We are also setting the animation direction depending upon the navigation direction that is determined by the state next.

Now as our layout is generated lets create the function for auto slide.

  const [current, setCurrent] = useState(0);
  const [next, setNext] = useState(true);

  const autoStart = useRef(null);
  const resume = useRef(null);

  //autoSlide
  const autoSlide = () => {
    autoStart.current = setInterval(() => {
      setCurrent(current + 1 >= list.length ? 0 : current + 1);
      setNext(true);
    }, 3500);
  };

  useEffect(() => {
     autoSlide();
  }, []);

As soon as the component is mounted we want the auto slide to start that is why we have call the auto slide function inside the useEffect hook.

Next, we want to change the slide when ever the navigation is clicked so lets do that.

  //Clear autoslide and resume
  const clear = () => {
    clearInterval(autoStart.current);
    clearTimeout(resume.current);
    autoStart.current = null;
    resume.current = null;
  };

  //Next slide
  const handleNext = () => {
    //Stop and Clear the auto slides
    clear();

    //Update the current index of slide
    setCurrent(current + 1 >= list.length ? 0 : current + 1);
    setNext(true);
  };

  const handlePrev = () => {
    //Stop and Clear the auto slides
    clear();

    //Update the current index of slide
    setCurrent(current - 1 < 0 ? list.length - 1 : current - 1);
    setNext(false);
  };

As you can see before changing the slide while navigating we are clearing the timer because we want the auto slide to stop and then set the current state appropriately.

But after the navigation is stopped for certain amount of time we want to resume the navigation. useEffect is the best life cycle method to resume the auto slide.

  useEffect(() => {  
    autoSlide();
     
    //If autoSlide is stopped
    //And in Idle state then resume autoslide
    if (!resume.current && !autoStart.current) {
      resume.current = setTimeout(() => {
        autoSlide();
      }, 1500);
    }
  }, [current, next]);

In then end clear the timers when the component is about to be removed or unmount.

  useEffect(() => {
    autoSlide();
    
    //If autoSlide is stopped
    //And in Idle state then resume autoslide
    if (!resume.current && !autoStart.current) {
      resume.current = setTimeout(() => {
        autoSlide();
      }, 1500);
    }
  
    //Clear on unmount
    return () => { clear(); };
  }, [current, next]);

Complete code of autoslide carousel component in React

import React, { useState, useEffect, useRef } from "react";
import styles from "./index.module.css";
import { useTransition, animated } from "@react-spring/web";

const list = [
    "https://cdn.pixabay.com/photo/2019/12/30/13/10/lost-places-4729640_1280.jpg",
    "https://cdn.pixabay.com/photo/2018/09/16/15/31/boy-3681679_1280.jpg",
    "https://cdn.pixabay.com/photo/2018/09/11/00/47/trunks-3668420_1280.jpg",
    "https://cdn.pixabay.com/photo/2018/11/14/12/13/young-3815082_1280.jpg",
    "https://cdn.pixabay.com/photo/2018/11/14/12/12/young-3815077_1280.jpg",
    "https://cdn.pixabay.com/photo/2018/09/15/11/19/male-3679138_1280.jpg",
    "https://cdn.pixabay.com/photo/2018/11/14/12/10/young-3815069_1280.jpg",
    "https://cdn.pixabay.com/photo/2018/11/16/00/20/young-3818476_1280.jpg"
  ];

const Carousel = () => {
  const [current, setCurrent] = useState(0);
  const [next, setNext] = useState(true);

  const autoStart = useRef(null);
  const resume = useRef(null);

  //AutoSlide
  const autoSlide = () => {
    autoStart.current = setInterval(() => {
      setCurrent(current + 1 >= list.length ? 0 : current + 1);
      setNext(true);
    }, 3500);
  };

  useEffect(() => {
    autoSlide();
    
    if (!resume.current && !autoStart.current) {
      resume.current = setTimeout(() => {
        autoSlide();
      }, 1500);
    }

    return () => { clear(); };
  }, [current, next]);

  //Clear autoslide and resume
  const clear = () => {
    clearInterval(autoStart.current);
    clearTimeout(resume.current);
    autoStart.current = null;
    resume.current = null;
  };

  //Next slide
  const handleNext = () => {
    //Stop and Clear the auto slides
    clear();

    //Update the current index of slide
    setCurrent(current + 1 >= list.length ? 0 : current + 1);
    setNext(true);
  };

  const handlePrev = () => {
    //Stop and Clear the auto slides
    clear();

    //Update the current index of slide
    setCurrent(current - 1 < 0 ? list.length - 1 : current - 1);
    setNext(false);
  };

  // Generate slides
  const animationDirection = next
    ? {
        from: { opacity: 0, transform: "translate3d(100%,0,0)" },
        enter: { opacity: 1, transform: "translate3d(0%,0,0)" },
        leave: { opacity: 0, transform: "translate3d(-50%,0,0)" }
      }
    : {
        from: { opacity: 0, transform: "translate3d(-50%,0,0)" },
        enter: { opacity: 1, transform: "translate3d(0%,0,0)" },
        leave: { opacity: 0, transform: "translate3d(100%,0,0)" }
      };

  const transitions = useTransition(current, {
    key: current,
    from: animationDirection.from,
    enter: animationDirection.enter,
    leave: animationDirection.leave,
  });

  const item = transitions((style, i) => (
    <animated.div
      className={"slide"}
      style={{
        ...style,
        backgroundImage: `url(${list[i]}`,
      }}
    />
  ));

  return (
    <>
      {/* Slides */}
      <div className={"wrapper"}>{item}</div> 

      {/* Controls */}
      <div className={"controls"}>
        <span onClick={handlePrev}>Prev</span>
        <span onClick={handleNext}>Next</span>
      </div>
    </>
  );
}

export default Carousel;

Complete style code of React autoslide carousel

I have the style for the carousel extremely simple, There is just a slide view area and below it is the navigation control.

.wrapper {
  display: flex;
  justify-content: center;
  align-self: center;
  width: 80%;
  margin: 0 auto;
  border: 2px solid;
  position: relative;
  height: 80vh;
  overflow: hidden;
}

.slide {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
  will-change: transform, opacity;
}

.controls {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 80%;
  margin: 25px auto 0 auto;
}

.controls > span {
  font-size: 2em;
  cursor: pointer;
}

Input

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import Carousel from "./Components/Carousel";
import * as serviceWorker from "./serviceWorker";

ReactDOM.render(
  <div className="abc">
    <Carousel />
  </div>,
  document.getElementById("root")
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

Output

React Slider Component