The source code for this library is available on GitHub.

Vaiva's Pattern Library.

Image Carousel

Carousel that takes the ImageCarouselItem component as a child to display swipeable images. By default, the images automatically swipe every few seconds and are paused when the mouse hovers over them.


        import React, { useEffect, useState } from 'react';
        import { useSwipeable } from 'react-swipeable';
        import styled from 'styled-components';
        
        const StyledImageCarousel = styled.div`
          .carousel {
            overflow: hidden;
          }
        
          .inner {
            white-space: nowrap;
            transition: transform 0.3s;
          }
        
          .indicators {
            display: flex;
            justify-content: space-between;
            padding: 0.5rem 0;
          }
        
          .indicators button {
            padding: 0;
            border: none;
            background: white;
            cursor: pointer;
        
            :hover {
              text-decoration: underline;
            }
          }
        
          .num-container > button {
            padding: 0.5rem;
            border-radius: 5px;
          }
        
          .num-container > button.active {
            background-color: red;
            color: #fff;
          }
        `;
        
        const StyledCarouselItem = styled.div`
          display: inline-flex;
          align-items: center;
          justify-content: center;
          min-height: 10rem;
          background: gray;
          color: white;
          width: 100%;
          height: 25rem;
        
          img {
            object-fit: cover;
            overflow: hidden;
            height: 100%;
            width: 100%;
          }
        `;
        
        export const ImageCarouselItem = ({ children }) => {
          return <StyledCarouselItem>{children}</StyledCarouselItem>;
        };
        
        const ImageCarousel = ({ children }) => {
          const [activeIndex, setActiveIndex] = useState(0);
          const [paused, setPaused] = useState(false);
        
          const updateIndex = newIndex => {
            if (newIndex < 0) {
              newIndex = React.Children.count(children) - 1;
            } else if (newIndex >= React.Children.count(children)) {
              newIndex = 0;
            }
        
            setActiveIndex(newIndex);
          };
        
          useEffect(() => {
            const interval = setInterval(() => {
              if (!paused) {
                updateIndex(activeIndex + 1);
              }
            }, 3000);
        
            return () => {
              if (interval) {
                clearInterval(interval);
              }
            };
          });
        
          const handlers = useSwipeable({
            onSwipedLeft: () => updateIndex(activeIndex + 1),
            onSwipedRight: () => updateIndex(activeIndex - 1),
          });
        
          return (
            <StyledImageCarousel>
              <div
                {...handlers}
                className="carousel"
                onMouseEnter={() => setPaused(true)}
                onMouseLeave={() => setPaused(false)}
              >
                <div
                  className="inner"
                  style={{ transform: `translateX(-${activeIndex * 100}%)` }}
                >
                  {React.Children.map(children, (child, index) => {
                    return React.cloneElement(child);
                  })}
                </div>
                <div className="indicators">
                  <button
                    onClick={() => {
                      updateIndex(activeIndex - 1);
                    }}
                  >
                    PREV
                  </button>
                  <div className="num-container">
                    {React.Children.map(children, (child, index) => {
                      return (
                        <button
                          className={`${index === activeIndex ? 'active' : ''}`}
                          onClick={() => {
                            updateIndex(index);
                          }}
                        >
                          {index + 1}
                        </button>
                      );
                    })}
                  </div>
                  <button
                    onClick={() => {
                      updateIndex(activeIndex + 1);
                    }}
                  >
                    NEXT
                  </button>
                </div>
              </div>
            </StyledImageCarousel>
          );
        };
        
        export default ImageCarousel;

More Components

TailwindCSS Text Carousel

The title speaks volumes.

Button

There is no description. Have a poem instead.

When I heard the learn’d astronomer,
When the proofs, the figures, were ranged in columns before me,
When I was shown the charts and diagrams, to add, divide, and measure them,
When I sitting heard the astronomer where he lectured with much applause in the lecture-room,
How soon unaccountable I became tired and sick,
Till rising and gliding out I wander’d off by myself,
In the mystical moist night-air, and from time to time,
Look’d up in perfect silence at the stars.