How to Build an Image Carousel in React JS 1024 877 Alina Kabanets

How to Build an Image Carousel in React JS

Let’s start this blog with something simple but fun. As I’m currently studying Frontend Development at Mate Academy Bootcamp, my current topic is React JS, following HTML, CSS, and JavaScript. This carousel is a small but cool project to implement.

 

Please note that here I focus primarily on React implementation and will describe only key points specific to the carousel. Therefore, don’t expect responsive design or step-by-step instructions. However, feel free to check the DEMO and the code in a Git Hub repository.

What is a Carousel?

A carousel, also known as an image slider or a slideshow, is a UI component that displays a series of images or content items in a rotating manner. It’s a popular choice for showcasing product images, testimonials, featured articles, and more.

The task

The task was to create a React component, a container with pictures that you can move by clicking on the buttons below it. The next step was adding “settings” to customize the carousel. You can change the picture’s width, how many pictures you want to be shown at once, how many items you want to be a step for moving, whether the moving will be infinite or not, and finally, you can set the animation duration.

The App Component

Here we have several useState hooks for a “Settings panel” to customize the Carousel:

const [itemWidth, setItemWidth] = useState(130);
const [frameSize, setFrameSize] = useState(3);
const [step, setStep] = useState(3);
const [animationDuration, setAnimationDuration] = useState(1000);
const [infinite, setInfinite] = useState(false);

We also have a fieldset with labels and inputs of different types for each setting, like this:

<fieldset className="App__settings">
        <legend>Settings</legend>
        <label htmlFor="itemId" className="App__label">
          {'Item width: '}
          <input
            type="number"
            id="itemId"
            min={30}
            max={260}
            step={10}
            onChange={(event) => {
              setItemWidth(+event.target.value);
            }}
            value={itemWidth}
          />
        </label>
</fieldset>

Each setting will become a Prop for the Carousel component:

<Carousel
        images={images}
        step={step}
        frameSize={frameSize}
        itemWidth={itemWidth}
        animationDuration={animationDuration}
        infinite={infinite}
      />

 We have the images as an array of strings like this:

const images = [
  './img/1.png',
  './img/2.png',
  './img/3.png',
  './img/4.png',
  './img/5.png',
  './img/6.png',
  './img/7.png',
  './img/8.png',
  './img/9.png',
  './img/10.png',
];

 

Carousel Component

As I study React with TypeScript, after creating the React functional component, we need to create a type for props and then use it in the Carousel component:

type Props = {
  images: string[];
  step: number;
  frameSize: number;
  itemWidth: number;
  animationDuration: number;
  infinite: boolean;
};

Now comes the most interesting part—actually implementing the Carousel. The trickiest part is understanding how to make the images move, and this is where translate-transform will help us. In CSS, we have the transform property, which can animate an HTML element in various ways. For our use case, we want to move our inner elements in the X (horizontal) direction by some specific pixels or percentage. For that, we use the translateX function. If we want our element to move to the right, the value passed would be positive and vice versa.

transform : translateX(50px); //moves elements to right by 50px
transform : translateX(-30px); //moves elements to left by 30px

transform : translateX(100%); //moves elements to right as far as its length
transform : translateX(-100%); //moves elements to left as far as its length

Then we create a hook for getting the current image index, starting from the first image in the array:

 

 const [currentImageIndex, setCurrentImageIndex] = useState(0);

We will get the total size we need to move the image left by doing the multiplication (as we can change element’s width in the “Settings” panel):

transform: `translateX(${-(currentImageIndex * itemWidth)}px)`

Then we add the transition effect to our images so they slide smoothly, giving a sliding effect. The whole thing looks like this:

<div
      className="Carousel"
      style={{
        width: `${frameSize * itemWidth}px`,
        transition: `${animationDuration}ms`,
      }}
    >
      <ul className="Carousel__list">
        {images.map((image, index) => (
          <li
            key={image}
            style={{
              width: frameSize * itemWidth,
              transform: `translateX(${-(currentImageIndex * itemWidth)}px)`,
              transition: `transform ${animationDuration}ms ease 0s`,
            }}
          >
            <img
              src={image}
              alt={`${index + 1}`}
              width={itemWidth}
            />
          </li>
        ))}
      </ul>
</div>

And that’s pretty much it!

Next-Prev Buttons Implementation

The next important part is implementing the next-prev buttons.

The calculation is pretty easy here:

const index = currentImageIndex - 1; // or currentImageIndex + 1 for moving right

But since we can control the step in Settings, for us it looks like this:

const index = currentImageIndex - step;

After that, normally we would set the hook – 

setCurrentImageIndex(index);

But as we have the “Settings” panel with adjustable options, the logic is a bit more complicated. We can change frame size (how many images we can see in the frame, visible ones), so we need to calculate maxImages – the first image of the last frame:

 const maxImages = images.length - frameSize;

We also need to handle the infinite option and disable buttons at the edges if needed. So the final piece is this:

const isDisabledPrev = currentImageIndex === 0 && !infinite;
  const isDisabledNext = currentImageIndex === maxImages && !infinite;

  function movePrev() {
    const index = currentImageIndex - step <= 0
      ? 0
      : currentImageIndex - step;

    const infiniteIndex = currentImageIndex === 0 && infinite
      ? maxImages
      : index;

    setCurrentImageIndex(infiniteIndex);
  }

  const moveNext = () => {
    const index = currentImageIndex + step >= maxImages
      ? maxImages
      : currentImageIndex + step;

    const infiniteIndex = currentImageIndex === maxImages && infinite
      ? 0
      : index;

    setCurrentImageIndex(infiniteIndex);
  };

And we use those functions on the prev-next buttons together with styling:

<div className="Carousel__controls">
        <button
          type="button"
          className={`Carousel__button ${
            isDisabledPrev
              ? 'Carousel__button--disabled'
              : 'Carousel__button--active'}`}
          onClick={movePrev}
        >
          &larr;
        </button>

        <button
          type="button"
          data-cy="next"
          className={`Carousel__button ${
            isDisabledNext
              ? 'Carousel__button--disabled'
              : 'Carousel__button--active'}`}
          onClick={moveNext}
        >
          &rarr;
        </button>
      </div>

Congratulations! We’ve implemented a great React JS Carousel! Now you can use it as a part of your bigger projects or just have fun coding and playing with CSS and React.

I’m sure it may not be the best solution (although it worked well for me), but there are no limits to perfection. So don’t hesitate to write in the comments your ways of how you would improve it.

 

Thank you for reading…

Happy Coding!!

  • Software engineering 1024 682 Alina Kabanets

Leave a Reply

Your email address will not be published.