React Spring Carousel

Thumb list

Let's go now a bit deeper in the carousel world and let's add some thumbnails:


  import { useSpringCarousel } from 'react-spring-carousel'

  export function Component() {
    const { 
      carouselFragment, 
      thumbsFragment,
      slideToPrevItem, 
      slideToNextItem 
    } = useSpringCarousel({
      withLoop: true,
      withThumbs: true, 
      items: mockedItems.map((i) => ({
        id: i.id,
        renderItem: (
          <CarouselItem color={i.color}>
            {i.title}
          </CarouselItem>
        ),
        renderThumb: (
          <CarouselThumb color={i.color}>
            {i.title}
          </CarouselThumb>
        )
      })),
    });

    return (
      <div>
        <button onClick={slideToPrevItem}>Prev item</button>
        {carouselFragment}
        <button onClick={slideToNextItem}>Next item</button>
        <div>{thumbsFragment}</div>
      </div>
    );
  }

Again, things are easier:

  • Add withThumbs: true
  • Add the property renderThumb like you do with renderItem
  • Destructure the thumbs fragment - thumbsFragment - and use it wherever you want!

As you can see, the thumbs wrapper will automatically scroll; the logic applied is that it will try to put the active item in the center of the container. Of course, if you want to, you can manually scroll since the scroll behavior is the default browser one.

Note: Since the library don't use any css library (the only styles that the library applies are inlined), if you want to hide the thumbs scrollBar, you'll have to do it by yourselve.

You can also disable the automatic scroll of the thumbs if you desire to; in case, just pass enableThumbsWrapperScroll: false

Slide to item

I know, let me guess: if you click on a thumb nothing happens, right? Yeah, that goes against the common pattern, that should slide the carousel to the clicked item. But as you may already know, I don't like to force people to act in a particular way; instead, I give total freedom to the developer who will, if is the case, replicate that pattern. But how? Check the next example 😏


  import { useSpringCarousel } from 'react-spring-carousel'

  export function Component() {
    const { 
      carouselFragment, 
      thumbsFragment,
      slideToPrevItem, 
      slideToNextItem,
      slideToItem // -> slide me to the moon
    } = useSpringCarousel({
      withLoop: true,
      withThumbs: true, 
      items: mockedItems.map((i) => ({
        id: i.id,
        renderItem: (
          <CarouselItem color={i.color}>
            {i.title}
          </CarouselItem>
        ),
        renderThumb: (
          <CarouselThumb 
            color={i.color} 
            onClick={() => slideToItem(i.id)}
          />
            {i.title}
          </CarouselThumb>
        )
      })),
    });

    return (
      <div>
        <button onClick={slideToPrevItem}>Prev item</button>
        {carouselFragment}
        <button onClick={slideToNextItem}>Next item</button>
        <div>{thumbsFragment}</div>
      </div>
    );
  }

prepareThumbsData

Let's say you want to display a beautiful high-quality images carousel; maybe you'll need to show images in buckets of 10 and, eventually, if the user wants to, load more and more images. If that's the case, well, React Spring Carousel makes it easy for you.

The useSpringCarousel hook has a property, prepareThumbsData, a function that receives the list of thumbs as the only argument, and it must return a list of thumbs as well. prepareThumbsData is executed before the rendering of the thumbs, so you can easily manipulate the list of items by adding or removing some items.

If the number of items change, and the new items are less than the previous one (it means that we remove some items), the carousel will automatically set as active the last existing item. Pretty cool uh? 😎

  import { useSpringCarousel } from 'react-spring-carousel'

  export function Component() {
    const [showExtraItems, setShowExtraItems] = useState(false);
    let items = [...mockedItems];

    if (showExtraItems) {
      items.push(...extraItems);
    } else {
      items = items.filter((i) => !i.id.includes("extra-item"));
    }

    const { 
      carouselFragment, 
      thumbsFragment, 
      slideToPrevItem, 
      slideToNextItem 
    } = useSpringCarousel({
      withThumbs: true,
      prepareThumbsData(items) {
        return [
          ...items,
          {
            id: "Button",
            renderThumb: (
              <button onClick={() => setShowExtraItems((p) => !p)}>
                Toggle items!
              </button>
            ),
          },
        ]
      },
      items: items.map((i) => ({
        id: i.id,
        renderItem: (
          <CarouselItem color={i.color}>
            {i.title}
          </CarouselItem>,
        )
        renderThumb: (
          <CarouselThumb color={i.color}>
            {i.title}
          </CarouselThumb>
        ),
      })),
    });

    return (
      <div>
        <button onClick={slideToPrevItem}>Prev item</button>
        {carouselFragment}
        <button onClick={slideToNextItem}>Next item</button>
        <div>{thumbsFragment}</div>
      </div>
    );
  }