• Stars
    star
    295
  • Rank 140,902 (Top 3 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created almost 2 years ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

DOM-first, headless carousel for React

React Snap Carousel 🫰

GitHub package.json version npm downloads CI GitHub license

DOM-first, headless carousel for React.

React Snap Carousel leaves the DOM in charge of scrolling and simply computes derived state from the layout, allowing you to progressively enhance a scroll element with responsive carousel controls.

Alt Text

🧈 Utilizes native browser scrolling & CSS scroll snap points for best performance and UX

πŸ“ Computes responsive page state from DOM layout & scroll position

πŸ“² Dynamic page-based CSS snap point rendering

πŸ™ˆ Headless design, giving you full control over UI using React Hooks API

πŸ–‹οΈ Written in TypeScript

πŸͺΆ Lightweight (~1kB) + zero dependencies

Install

npm install react-snap-carousel

Resources

πŸ”₯StoryBook ExamplesπŸ”₯

CodeSandbox StarterKit

Beginners Tutorial

Usage

React Snap Carousel doesn't expose a ready-made <Carousel /> component and instead offers a single export useSnapCarousel which provides the state & functions necessary to build your own carousel component.

The following code snippet is a good starting point.

Inline styles are used for simplicity. You can use whichever CSS framework you prefer.

You can see it in action on CodeSandbox.

// Carousel.tsx
import React, { CSSProperties } from 'react';
import { useSnapCarousel } from 'react-snap-carousel';

const styles = {
  root: {},
  scroll: {
    position: 'relative',
    display: 'flex',
    overflow: 'auto',
    scrollSnapType: 'x mandatory'
  },
  item: {
    width: '250px',
    height: '250px',
    flexShrink: 0
  },
  itemSnapPoint: {
    scrollSnapAlign: 'start'
  },
  controls: {
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center'
  },
  nextPrevButton: {},
  nextPrevButtonDisabled: { opacity: 0.3 },
  pagination: {
    display: 'flex'
  },
  paginationButton: {
    margin: '10px'
  },
  paginationButtonActive: { opacity: 0.3 },
  pageIndicator: {
    display: 'flex',
    justifyContent: 'center'
  }
} satisfies Record<string, CSSProperties>;

interface CarouselProps<T> {
  readonly items: T[];
  readonly renderItem: (
    props: CarouselRenderItemProps<T>
  ) => React.ReactElement<CarouselItemProps>;
}

interface CarouselRenderItemProps<T> {
  readonly item: T;
  readonly isSnapPoint: boolean;
}

export const Carousel = <T extends any>({
  items,
  renderItem
}: CarouselProps<T>) => {
  const {
    scrollRef,
    pages,
    activePageIndex,
    prev,
    next,
    goTo,
    snapPointIndexes
  } = useSnapCarousel();
  return (
    <div style={styles.root}>
      <ul style={styles.scroll} ref={scrollRef}>
        {items.map((item, i) =>
          renderItem({
            item,
            isSnapPoint: snapPointIndexes.has(i)
          })
        )}
      </ul>
      <div style={styles.controls} aria-hidden>
        <button
          style={{
            ...styles.nextPrevButton,
            ...(activePageIndex <= 0 ? styles.nextPrevButtonDisabled : {})
          }}
          onClick={() => prev()}
        >
          Prev
        </button>
        {pages.map((_, i) => (
          <button
            key={i}
            style={{
              ...styles.paginationButton,
              ...(activePageIndex === i ? styles.paginationButtonActive : {})
            }}
            onClick={() => goTo(i)}
          >
            {i + 1}
          </button>
        ))}
        <button
          style={{
            ...styles.nextPrevButton,
            ...(activePageIndex === pages.length - 1
              ? styles.nextPrevButtonDisabled
              : {})
          }}
          onClick={() => next()}
        >
          Next
        </button>
      </div>
      <div style={styles.pageIndicator}>
        {activePageIndex + 1} / {pages.length}
      </div>
    </div>
  );
};

interface CarouselItemProps {
  readonly isSnapPoint: boolean;
  readonly children?: React.ReactNode;
}

export const CarouselItem = ({ isSnapPoint, children }: CarouselItemProps) => (
  <li
    style={{
      ...styles.item,
      ...(isSnapPoint ? styles.itemSnapPoint : {})
    }}
  >
    {children}
  </li>
);
// App.tsx
import { Carousel, CarouselItem } from './Carousel';

const items = Array.from({ length: 20 }).map((_, i) => ({
  id: i,
  src: `https://picsum.photos/500?idx=${i}`
}));

const App = () => (
  <Carousel
    items={items}
    renderItem={({ item, isSnapPoint }) => (
      <CarouselItem key={item.id} isSnapPoint={isSnapPoint}>
        <img src={item.src} width="250" height="250" alt="" />
      </CarouselItem>
    )}
  />
);

export default App;

Api

useSnapCarousel(options)

Parameters

Options
interface SnapCarouselOptions {
  // Horizontal or vertical carousel
  readonly axis?: 'x' | 'y';
  // Allows you to render pagination during SSR
  readonly initialPages?: number[][];
}

Return value

export interface SnapCarouselResult {
  readonly pages: number[][];
  readonly activePageIndex: number;
  readonly snapPointIndexes: Set<number>;
  readonly prev: () => void;
  readonly next: () => void;
  readonly goTo: (pageIndex: number) => void;
  readonly refresh: () => void;
  readonly scrollRef: (el: HTMLElement | null) => void;
}

License

MIT

More Repositories

1

jquery-ui-carousel

jQuery RS Carousel is a responsive and touch-enabled carousel written on top of jQuery and the jQuery UI Widget Factory providing a full and familiar API in less than 2.6kB minified and gzipped.
JavaScript
192
star
2

ok-computer

Ξ» "Functions all the way down" data validation for JavaScript and TypeScript.
TypeScript
79
star
3

snippets

Chrome extension allowing you to import and execute JavaScript code snippets from GitHub
JavaScript
66
star
4

jquery-carousel

A flexible and unobtrusive Carousel plugin
JavaScript
40
star
5

jquery-mousestop-event

Offers a less intrusive alternative to JavaScripts mouseenter event
JavaScript
18
star
6

jquery-modal

This was written to address some issues I've experienced when working with a number of existing modal plugins
JavaScript
18
star
7

bugz

πŸ› Composable User Agent Detection using Ramda
JavaScript
17
star
8

require-error-handler-webpack-plugin

Adds error callback to require.ensure and AMD require calls.
JavaScript
14
star
9

react-teleportal

Alternative React portal implementation, giving you control over portal rendering.
TypeScript
6
star
10

nextjs-shopify-app

Shopify app boilerplate using Next.js
TypeScript
5
star
11

react-render-to-string-async

Non-blocking asynchronous alternative to `React.renderToString` and `React.renderToStaticMarkup`.
HTML
3
star
12

jquery-in-field-labels

unobtrusive method to add labels to form inputs that hide on focus and show on blur (also supports Password inputs)
JavaScript
3
star
13

redux-form-v5-vs-redux-form-v6

A performance comparison of redux-form v5 vs v6
JavaScript
2
star
14

request-idle-callback-demo

JavaScript
2
star
15

backbone-ajax-queue

Backbone Ajax Queue updates `Backbone.sync` to allow ajax requests to optionally be queued to help support the development of optimistic, asynchronous user interfaces as described here - http://alexmaccaw.com/posts/async_ui
JavaScript
2
star
16

browser-console-build-error-webpack-plugin

Outputs webpack build errors in the browser console.
JavaScript
1
star
17

react-webpack-boilerplate

A react boilerplate using webpack
JavaScript
1
star
18

remix-shopify-customer-account-api

TypeScript
1
star