Build Your Own Carousel Component with React Snap Carousel
Tags: react, javascript, tailwindcss, tutorial
Published: 2023-02-14 18:13:51

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](https://github.com/richardscarrott/react-snap-carousel).

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

```javascript
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>
  );
};
```

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.

```javascript
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>
  );
};
```

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

```javascript
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>
    </>
  );
};
```

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

```javascript
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>
    </>
  );
};
```

Voilà, a fully functioning carousel component 🥳.

Final code on [CodeSandbox](https://codesandbox.io/s/react-snap-carousel-dev-to-tutorial-2ni9kj?file=/src/Carousel.tsx).

> 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](https://github.com/richardscarrott/react-snap-carousel#api).
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](https://richardscarrott.github.io/react-snap-carousel/).