DEV Community

Cover image for Build Your Own Carousel Component with React Snap Carousel
Richard Scarrott
Richard Scarrott

Posted on

Build Your Own Carousel Component with React Snap Carousel

Carousel UIs can be a great way to present lists of content without taking up too much precious real estate.

Today we'll build our own responsive carousel component with a little help from react-snap-carousel.

First up, create a basic Carousel component rendering a list of scrollable images.

export const Carousel = () => {
  return (
    <ul className="flex overflow-x-auto">
      {Array.from({ length: 18 }).map((_, i) => (
        <li key={i} className="flex-shrink-0">
          <img
            src={`https://picsum.photos/500?${i}`}
            width="250"
            height="250"
            alt={`Item ${i}`}
          />
        </li>
      ))}
    </ul>
  );
};
Enter fullscreen mode Exit fullscreen mode

Okay let's face it, you've got yourself an MVP Carousel already so you could stop here 🙌. However, with a little bit of JavaScript we can progressively enhance our carousel with some nice-to-have features.

Firstly, let's add CSS scroll snap points so the user can swipe between each "page" of items.

import { useSnapCarousel } from "react-snap-carousel";

export const Carousel = () => {
  const { scrollRef, snapPointIndexes } = useSnapCarousel();
  return (
    <ul className="flex overflow-x-auto snap-x snap-mandatory" ref={scrollRef}>
      {Array.from({ length: 18 }).map((_, i) => (
        <li
          key={i}
          className="flex-shrink-0"
          style={{
            scrollSnapAlign: snapPointIndexes.has(i) ? "start" : ""
          }}
        >
          <img
            src={`https://picsum.photos/500?${i}`}
            width="250"
            height="250"
            alt={`Item ${i}`}
          />
        </li>
      ))}
    </ul>
  );
};
Enter fullscreen mode Exit fullscreen mode

Secondly, let's add some controls to navigate from one page to the next.

import { useSnapCarousel } from "react-snap-carousel";

export const Carousel = () => {
  const { scrollRef, snapPointIndexes, next, prev } = useSnapCarousel();
  return (
    <>
      <ul
        className="flex overflow-x-auto snap-x snap-mandatory"
        ref={scrollRef}
      >
        {Array.from({ length: 18 }).map((_, i) => (
          <li
            key={i}
            className="flex-shrink-0"
            style={{
              scrollSnapAlign: snapPointIndexes.has(i) ? "start" : ""
            }}
          >
            <img
              src={`https://picsum.photos/500?${i}`}
              width="250"
              height="250"
              alt={`Item ${i}`}
            />
          </li>
        ))}
      </ul>
      <div className="flex justify-center space-x mt-2" aria-hidden>
        <button
          className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mx-2"
          onClick={() => prev()}
        >
          Previous
        </button>
        <button
          className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
          onClick={() => next()}
        >
          Next
        </button>
      </div>
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

Lastly, let's add some pagination controls to allow users to jump to a specific page.

import { useSnapCarousel } from "react-snap-carousel";

export const Carousel = () => {
  const {
    scrollRef,
    snapPointIndexes,
    next,
    prev,
    pages,
    goTo
  } = useSnapCarousel();
  return (
    <>
      <ul
        className="flex overflow-x-auto snap-x snap-mandatory"
        ref={scrollRef}
      >
        {Array.from({ length: 18 }).map((_, i) => (
          <li
            key={i}
            className="flex-shrink-0"
            style={{
              scrollSnapAlign: snapPointIndexes.has(i) ? "start" : ""
            }}
          >
            <img
              src={`https://picsum.photos/500?${i}`}
              width="250"
              height="250"
              alt={`Item ${i}`}
            />
          </li>
        ))}
      </ul>
      <div className="flex justify-center space-x mt-2" aria-hidden>
        <button
          className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mx-2"
          onClick={() => prev()}
        >
          Previous
        </button>
        {pages.map((_, i) => (
          <button
            key={i}
            className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-2 rounded mx-2"
            onClick={() => goTo(i)}
          >
            {i + 1}
          </button>
        ))}
        <button
          className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
          onClick={() => next()}
        >
          Next
        </button>
      </div>
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

Voilà, a fully functioning carousel component 🥳.

Final code on CodeSandbox.

TIP: Resize your browser to see how the CSS snap points and pagination dynamically update depending on how many items fit in the viewport.

If you want to take it further, you might want to consider the following:

  1. Highlight the active page using activePageIndex provided by React Snap Carousel.
  2. Disable the previous / next buttons when on the first / last page respectively. Again using activePageIndex.
  3. Hide the scrollbars with CSS.
  4. Make your carousel component reusable by accepting items as a prop.

You can find some inspiration by checking out more examples using React Snap Carousel here.

Top comments (0)