A highly impartial suite of React components that can be assembled by the consumer to create a carousel with almost no limits on DOM structure or CSS styles. If you're tired of fighting some other developer's CSS and DOM structure, this carousel is for you.

Overview

Pure React Carousel

Created By
Express Logo

A highly impartial suite of React components that can be assembled by the consumer to create a responsive and aria compliant carousel with almost no limits on DOM structure or CSS styles.

See Live Examples | See Example Code


Build Status Known Vulnerabilities version downloads MIT License

All Contributors PRs Welcome Code of Conduct

Motivation

My goal was to create a 100% ReactJS carousel that doesn't try to impose structure or styles that need to be defeated in order to match your site's design standards. Are you tired of fighting some other developer's CSS or DOM structure? If so, this carousel is for you.

Carousels: Love them or hate them. However, if you are a React developer, and you have to use a carousel, why not use one that was...

  • Developed from the ground-up in React.
  • Is not a wrapper or port of some non-react carousel like Slick or Flickity.
  • Fully supports touch events.
  • Is aria compliant.
  • Is responsive by default.
  • Lets you assemble the carousel components in the DOM in any order you desire so long as they are all children of a single <CarouselProvider /> component.
  • Lets you put any class names, properties, attributes, or styles on any of the components that you need.
  • Supports ES6 and commonjs.
  • Has 100% test coverage. Solid!

Table of contents

πŸ›  Tutorial

Let's make a simple carousel with three slides, a next button, and a back button.

  1. Add the module to your project.

npm i -S pure-react-carousel

  1. Import only the required components into your project.
import React from 'react';
import { CarouselProvider, Slider, Slide, ButtonBack, ButtonNext } from 'pure-react-carousel';
  1. Using Webpack or Rollup? Does your Webpack config have a loader for "css" files? If so, import the css as well.
import React from 'react';
import { CarouselProvider, Slider, Slide, ButtonBack, ButtonNext } from 'pure-react-carousel';
import 'pure-react-carousel/dist/react-carousel.es.css';
  1. Put a CarouselProvider component in your code. This allows the other carousel components to communicate with each other. The only required properties are the orientation, naturalSlideWidth, and naturalSlideHeight. The naturalSlideWidth and naturalSlideHeight are used to create an aspect ratio for each slide. Since the carousel is responsive by default, it will stretch to fill in the width of it's parent container. The CarouselProvider must also have children. We'll add the children in the next step.
import React from 'react';
import { CarouselProvider, Slider, Slide, ButtonBack, ButtonNext } from 'pure-react-carousel';
import 'pure-react-carousel/dist/react-carousel.es.css';

export default class extends React.Component {
  render() {
    return (
      <CarouselProvider
        naturalSlideWidth={100}
        naturalSlideHeight={125}
        totalSlides={3}
      ></CarouselProvider>
    );
  }
}
  1. Place the components in any order you wish as long as they are children of a single CarouselProvider component. Some components have ancestor/descendant relationships but they don't have to be direct relatives. For example: Slides need to go inside of a Slider. Slides also require a sequentially numbered index prop starting at zero. Chances are, a lot of the time, you're going to be populating the slides from data and looping through the data, so it would be easy to add an index in your loop.
import React from 'react';
import { CarouselProvider, Slider, Slide, ButtonBack, ButtonNext } from 'pure-react-carousel';
import 'pure-react-carousel/dist/react-carousel.es.css';

export default class extends React.Component {
  render() {
    return (
      <CarouselProvider
        naturalSlideWidth={100}
        naturalSlideHeight={125}
        totalSlides={3}
      >
        <Slider>
          <Slide index={0}>I am the first Slide.</Slide>
          <Slide index={1}>I am the second Slide.</Slide>
          <Slide index={2}>I am the third Slide.</Slide>
        </Slider>
      </CarouselProvider>
    );
  }
}
  1. Add some buttons so the user can navigate forward and backwards.
import React from 'react';
import { CarouselProvider, Slider, Slide, ButtonBack, ButtonNext } from 'pure-react-carousel';
import 'pure-react-carousel/dist/react-carousel.es.css';

export default class extends React.Component {
  render() {
    return (
      <CarouselProvider
        naturalSlideWidth={100}
        naturalSlideHeight={125}
        totalSlides={3}
      >
        <Slider>
          <Slide index={0}>I am the first Slide.</Slide>
          <Slide index={1}>I am the second Slide.</Slide>
          <Slide index={2}>I am the third Slide.</Slide>
        </Slider>
        <ButtonBack>Back</ButtonBack>
        <ButtonNext>Next</ButtonNext>
      </CarouselProvider>
    );
  }
}

That's it. You have a super basic Carousel.

There are other components you can add, like ButtonFirst, ButtonLast, an Image component, and even an ImageWithZoom component that zooms on mouse hover or finger tap.

Obviously, you can customize the heck out of the layout. If you need to bury your Slider component in 18 parent divs, go for it. It will still do it's job. Feel free to add the className property to any of the Components to further customize your carousel. Or, hook into the many BEM named default CSS class names built into the carousel components.

Some components have a ancestor / descendant relationship but they don't have to be direct descendants of the parent. For example, Slide needs to be a descendant of Slider, but you can put a bunch of div wrappers around slide if you need to. A good analogy are the html tags table and tr. The tr tag needs to be a descendant of table, but it doesn't have to be a direct descendant. You can have a tbody between them in the tree.

Component Properties (props)

classname

You can attach your own className property to each and every component in this library and it will be appended to the list of classes. It's appended so that it has more specificity in a tie, allowing your CSS to more easily override any internal styles without resorting to using !important.

styles

You can attach your own styles property to each component in this library, however, any styles generated by the component take precedence over any styles you provide. Some components, like the <Slider />, need their internal styles to function correctly.

event props (onClick, onFocus, onBlur, etc)

You can supply your own event callbacks to any component. Your event callbacks are called after a component's internal event handling. Basically, your callback becomes a callback to our callback. Say that 10 times fast. :-)

all other props

Any remaining props not consumed by the component are passed directly to the root element of a component unless otherwise noted in that component's documentation. This makes all the components in this library HIGHLY configurable. You can, for example, add your own event handlers, or change aria tags, etc.

Components

<CarouselProvider />

property type default required purpose
children [string|node] Yes Children is a special React property. Basically, the CarouselProvider needs to wrap other Carousel components and JSX
className [string|null] null No Optional className string that will be appended to the component's className string
currentSlide number 0 No <Slide > to display ONLY when CarouselProvider mounts. The indexing of <Slide /> components starts with 0. This is a poorly named variable and will be deprecated in a future version.
hasMasterSpinner bool false No When true, a spinner will cover <Slider /> component until all <Image > and <ImageWithZoom > are done loading images. If there are no <Image /> or <ImageWithZoom> components, the spinner will spin until this property is set to false
interval number 5000 No Number of milliseconds to wait when the auto slideshow is active
isPlaying bool false No Setting this to true starts an auto slideshow. After "interval" milliseconds, the slider will move by "step" slides either forward or backwards depending on the value of "playDirection".
lockOnWindowScroll bool false No When set to true, scrolling of the carousel slides are disabled while the browser window is scrolling
naturalSlideHeight number Yes The natural height of each <\Slide > component. **
naturalSlideWidth number Yes The natural width of each <\Slide > component. **
orientation string "horizontal" No Possible values are "horizontal" and "vertical". Let's you have a horizontal or vertical carousel.
playDirection ['forward'|'backward' ] 'forward' No The direction for the auto slideshow
step number 1 No The number of slides to move when pressing the <ButtonBack /> and <ButtonNext /> buttons.
dragStep number 1 No The number of slides to move when performing a short touch drag.
tag string 'div' No The HTML element to use for the provider.
totalSlides number Yes Always set this to match the total number of <Slide > components in your carousel
touchEnabled boolean true No Set to true to enable touch events
dragEnabled boolean true No Set to true to enable mouse dragging events
visibleSlides number 1 No The number of slides to show at once. This number should be <= totalSlides
infinite boolean false No Should the carousel continue or stop at the beginning or end of the slides
isIntrinsicHeight boolean false No Disables the enforced height ratio, and instead uses the intrinsic height of the slides. This option can only be active in horizontal orientation, it will throw an error in vertical orientation.

The CarouselProvider component creates the following pseudo HTML by default:

<props.tag|div class="carousel [props.className]" ...props>
  [props.children]
</props.tag|div>

More about naturalSlideWidth and naturalSlideHeight The carousel is responsive and by default will flex to the full width of the <Slider > parent container. It's up to you to contain the carousel width via css. Each slide will be the same height to width ratio (intrinsic ratio). CarouselProvider needs to know the default size of each <Slide />. Note: you can make the carousel non-responsive by setting the width of <Slider >to a fixed css unit, like pixels. There are many other ways to make the carousel non-responsive.

<Slider />

A Slider is a viewport that masks slides. The Slider component must wrap one or more Slide components.

property type default required purpose
children [string|node] Yes Children is a special React property. Basically, the Slider needs to wrap other components and/or JSX
ariaLabel string 'slider' N Optional sting that sets the "aria-label" attribute.
className [string|null] null No Optional className string that will be appended to the component's className string.
classNameAnimation [string|null] null No Optional className string. The slider uses the css transform property, applying translateX to move the slider tray east and west for a horizontal slider, and translateY to move the slider north and south for a vertical slider. The actual animation is the result of applying a CSS3 transition effect. If you supply your own classNameAnimation class, the default transition is disabled and ONLY the transitions specified by the classNameAnimation class are applied. Learn more about CSS3 transitions.
classNameTray [string|null] null No Optional className string that is applied to the Slider's tray. The "tray" is the DOM element that contains the slides. The type of DOM element is specified by the trayTag property
classNameTrayWrap [string|null] null No Optional className string that is applied to a div that surrounds the Slider's tray
moveThreshold number 0.1 No Threshold to control the drag distance that triggers a scroll to the next or previous slide. (slide width or height * moveThreshold = drag pixel distance required to scroll)
onMasterSpinner [function|null] null No Optional callback function that is called when the Master Spinner is visible. Requires that <CarouselProvider /> set hasMasterSpinner to true
spinner function null No Optional inline JSX (aka "render props") to render your own custom spinner. Example () => <MyCustomSpinnerComponent /> If left blank, the default spinner is used.
style object {} No Optional css styles to add to the Slider. Note: internal css properties take precedence over any styles specified in the styles object
trayProps object {} No Any props you want to attach to the slider tray with the exception of className and style. The className prop is handled via classNameTray prop above. Style is used internally. Any event handlers like onMouseDown or others are called after any of our internal event handlers.
trayTag string 'div' No The HTML tag to used for the tray (the thing that holds all the slides and moves the slides back and forth.)

The Slider component creates the following pseudo HTML by default.

<div class="carousel__slider [carousel__slider--vertical|carousel__slider--horizontal] [props.className]" aria-live="polite" style="[props.style]" ...props>
  <div class="carousel__slider-tray-wrapper [carousel__slider-tray-wrap--vertical|carousel__slider-tray-wrap--horizontal][props.classNameTrayWrap]">
    <props.trayTag|div class="carousel__slider-tray [props.classNameAnimation] [carousel__slider-tray--vertical|carousel__slider-tray--horizontal] [props.classNameTray]">
      [props.children]
    </props.trayTag|div>
    <div class="carousel__master-spinner-container">
      <div class="carousel__spinner" />
    </div>
  </div>
</div>

How to Change the Default Transition Animation

  • Read the documentation for the classNameAnimation property on the Slider component.
  • Read about CSS3 transitions.
  • Create your own CSS class that uses a CSS3 transition.
  • Pass the CSS class you create to the classNameAnimation property of Slider.

<Slide />

The Slide component is a container with an intrinsic ratio computed by the CarouselProvider naturalSlideWidth and naturalSlideHeight properties. By default, only one slide is visible in the Slider at a time. You can change this by altering the visibleSlides property of the CarouselProvider. Slide components also contain a div that acts as an aria compliant focus ring when the Slide receives focus either by using a keyboard tab, mouse click, or touch.

property type default required purpose
className [string|null] null No Optional className string that will be appended to the component's className string.
ariaLabel string 'slide' N Optional sting that sets the "aria-label" attribute.
classNameHidden [string|null] null No Optional className string that will be appended to the component's className string when the slide is not visible.
classNameVisible [string|null] null No Optional className string that will be appended to the component's className string when the slide is visible.
index number Yes You must consecutively number the <Slide > components in order starting with zero.
innerClassName [string|null] null No Optional className string that will be appended to an internal HTML element created by the Component. Best to just use Chrome Dev Tools to inspect the demo app or check the source code for <Slide />
innerTag string 'div' No The inner HTML element for each Slide.
onBlur [function|null] null No Optional callback function that is called after the internal onBlur function is called. It is passed the React synthetic event
onFocus [function|null] null No Optional callback function that is called after the internal onFocus function is called. It is passed the React synthetic event
tabIndex [number|null] null No When null, the Carousel will set this automatically. 99.9% of the time, you're going to want to leave this alone and let the carousel handle tabIndex automatically.
tag string 'div' No The root HTML element for each Slide.

The Slide component creates the following pseudo HTML by default:

<props.tag|div class="carousel__slide [carousel__slide--focused] [props.className] [props.classNameVisible|props.classNameHidden] [carousel__slide--hidden|carousel__slide--visible]" tabIndex="[props.tabIndex]" aria-hidden="[computed internally]" onFocus="[props.onFocus]" onBlur="[props.onBlur]" style="[props.style]" ...props>
  <props.innerTag|div class="carousel__inner-slide [props.innerClassName]">
    [props.children]
    <div class="carousel__slide-focus-ring" />
  <props.innerTag|div>
</props.tag|div>

<Dot />

A Dot component is a HTML button. Dots directly correlate to slides. Clicking on a dot causes it's correlating slide to scroll into the left-most visible slot of slider. The dots for currently visible slides cause are disabled. You can override the auto-disable feature by setting disabled to false (see table below)

property type default required purpose
children [string|null|node] null No Children is a special React property. Basically, the Dot component wraps other components and/or JSX
className [string|null] null No Optional className string that will be appended to the component's className string.
disabled [boolean|null] null No Null means Dot will automatically determine if this button is disabled. Setting this to true will force the button to be disabled. Setting this to false will prevent the button from ever being disabled.
onClick [function|null] null No Optional callback function that is called after the internal onClick function is called. It is passed the React synthetic event
slide number Yes There must be a matching <Slide /> component with a matching index property. Example: <Dot slide={0} /> will match <Slide index={0} />

The Dot component creates the following pseudo HTML by default:

<button class="carousel__dot carousel__dot--[slide] [carousel__dot--selected] [props.className]" onClick="[props.onClick]" disabled="[props.disabled]" ...props>
  [props.children]
</button>

<DotGroup />

A compound component that creates a bunch of Dot's automatically for you.

property type default required purpose
children [string|node|null] null No Any JSX wrapped by this component will appear AFTER the dots.
className [string|null] null No Optional className string that will be appended to the component's className string.
dotNumbers boolean false No Setting to true automatically adds text numbers the dot buttons starting at 1.
disableActiveDots boolean true No Setting to true make all dots, including active dots, enabled.
showAsSelectedForCurrentSlideOnly boolean false No Setting to true show only the current slide dot as selected.
renderDots function null No It accepts props and overrides renderDots() in .

The DotGroup component creates the following pseudo HTML by default:

<div class="carousel__dot-group [props.className]" ...props>
  <!-- button repeats for each slide -->
  <button class="carousel__dot carousel__dot--[slide] [carousel__dot--selected]">
    [numbers or blank]
  </button>
  [props.children]
</div>

<Image />

property type default required purpose
alt string "" No Specifies an alternate text for an image, if the image cannot be displayed.
children [string|node|null] null Yes Any optional JSX wrapped by the Image component
className [string|null] null No Optional className string that will be appended to the component's className string.
hasMasterSpinner bool Yes **If set to true, a spinner will cover the entire slider viewport until all Image components with hasMasterSpinner set to true are finished loading images. It only takes one Image component with hasMasterSpinner to enable the master spinner. **
isBgImage bool false No Setting this to true makes the image load as a background image. Any child JSX (see children property) will appear on top of the image. If set to false, no image will appear unless your child JSX displays the image via an image tag or some other means.
onError [func|null] null No Callback function called if the image defined by the src property fails to load. This Callback is fired after the Image component's internal handleImageError method.
onLoad [func|null] null No Callback function called if the image defined by the src property loads successfully. This Callback is fired after the Image component's internal renderSuccess method.
renderError [func|null] null No When defined, if an image fails to load, this function is called. It must return JSX which will be rendered instead of the broken image.
renderLoading [func|null] null No When defined, this function is called while the image is loading. It must return JSX which will be rendered instead of the loading image.
src string Yes URL of the image
tag string "img" No The element that will receive the image. Another option might be to set this to "div". Any tag besides "img" will result in the image being loaded as the css background-image for that tag.

<ButtonBack />

A button for moving the slider backwards. Backwards on a horizontal carousel means "move to the left". Backwards on a vertical carousel means "move to the top". The slider will traverse an amount of slides determined by the step property of CarouselProvider.

property type default required purpose
children [string|node] Yes Children is a special React property. Basically, the ButtonBack component needs to wrap other components and/or JSX
className [string|null] null No Optional className string that will be appended to the component's className string.
disabled [boolean|null] null No Null means ButtonBack will automatically determine if this button is disabled. Setting this to true will force the button to be disabled. Setting this to false will prevent the button from ever being disabled.
onClick [function|null] null No Optional callback function that is called after the internal onClick function is called. It is passed the React synthetic event

The ButtonBack component creates the following pseudo HTML by default:

<button class="carousel__back-button [props.className]" onClick="[props.onClick]" disabled="[props.disabled]" ...props>
  [props.children]
</button>

<ButtonNext />

A button for moving the slider forwards. Forwards on a horizontal carousel means "move to the right". Backwards on a vertical carousel means "move to the bottom". The slider will traverse an amount of slides determined by the step property of CarouselProvider.

property type default required purpose
children [string|node] Yes Children is a special React property. Basically, the ButtonNext component needs to wrap other components and/or JSX
className [string|null] null No Optional className string that will be appended to the component's className string.
disabled [boolean|null] null No Null means ButtonNext will automatically determine if this button is disabled. Setting this to true will force the button to be disabled. Setting this to false will prevent the button from ever being disabled.
onClick [function|null] null No Optional callback function that is called after the internal onClick function is called. It is passed the React synthetic event

The ButtonNext component creates the following pseudo HTML by default:

<button class="carousel__next-button [props.className]" onClick="[props.onClick]" disabled="[props.disabled]" ...props>
  [props.children]
</button>

<ButtonFirst />

Moves the slider to the beginning of the slides.

property type default required purpose
children [string|node] Yes Children is a special React property. Basically, the ButtonFirst component needs to wrap other components and/or JSX
className [string|null] null No Optional className string that will be appended to the component's className string.
disabled [boolean|null] null No Null means ButtonFirst will automatically determine if this button is disabled. Setting this to true will force the button to be disabled. Setting this to false will prevent the button from ever being disabled.
onClick [function|null] null No Optional callback function that is called after the internal onClick function is called. It is passed the React synthetic event

The ButtonFirst component creates the following pseudo HTML by default:

<button class="carousel__first-button [props.className]" onClick="[props.onClick]" disabled="[props.disabled]" ...props>
  [props.children]
</button>

<ButtonLast />

Moves the slider to the end of the slides (totalSlides - visibleSlides).

property type default required purpose
children [string|node] Yes Children is a special React property. Basically, the ButtonLast component needs to wrap other components and/or JSX
className [string|null] null No Optional className string that will be appended to the component's className string.
disabled [boolean|null] null No Null means ButtonLast will automatically determine if this button is disabled. Setting this to true will force the button to be disabled. Setting this to false will prevent the button from ever being disabled.
onClick [function|null] null No Optional callback function that is called after the internal onClick function is called. It is passed the React synthetic event

The ButtonLast component creates the following pseudo HTML by default:

<button class="carousel__last-button [props.className]" onClick="[props.onClick]" disabled="[props.disabled]" ...props>
  [props.children]
</button>

<ButtonPlay />

Pressing this button causes the slides to automatically advance by CarouselProvider's step property after an interval determined by CarouselProvider's interval property.

property type default required purpose
children [string|node] null No Children is a special React property. Content wrapped by ButtonPlay will appear AFTER the content of childrenPaused and childrenPlaying
childrenPaused [string|node] null No Content to display when the slide show is paused.
childrenPlaying [string|node] null No Content to display when the slide show is playing
className [string|null] null No Optional className string that will be appended to the component's className string.
disabled [boolean|null] null No Null means ButtonPlay will automatically determine if this button is disabled. Setting this to true will force the button to be disabled. Setting this to false will prevent the button from ever being disabled.
onClick [function|null] null No Optional callback function that is called after the internal onClick function is called. It is passed the React synthetic event

The ButtonPlay component creates the following pseudo HTML by default:

<button class="carousel__play-button [props.className]" onClick="[props.onClick]" disabled="[props.disabled]" ...props>
  [props.childrenPaused]
  [props.childrenPlaying]
  [props.children]
</button>

WithStore() Higher Order Component

NOTE: ADVANCED USE ONLY.

Use this HOC to pass CarouselProvider state properties as props to a component. Basically, Your custom component must be an descendant of <CarouselProvider>. It doesn't have to be a direct descendant, it just needs to be between some the opening and closing CarouselProvider tags somewhere. For example...

// pseudocode example
<CarouselProvider>
  <YourComponentHere />
</CarouselProvider>

WithStore has two arguments:

WithStore([component], [mapstateToProps])

  • The first argument is the component to wrap (ex: YourComponentHere) and it's required.

  • The second argument is optional. It is a "map state to props" function that you must create. This function maps the state of the CarouselProvider to props used by your component. Your "map state to props" function will receive one argument: an object with the current CarouselProvider state. Your function must return an object where the keys are names of props to pass to your component and the values map to properties of the CarouselProvider's state.

Here's more pseudocode. I've listed a bunch of properties that exist in the CarouselProvider.

  import React from 'react';
  import { WithStore } from 'pure-react-carousel';

  class YourComponentHere extends React.Component {
    // ... stuff
  }

  export default WithStore(YourComponentHere, state => ({
    // these are read only properties.  we use the "deepFreeze"
    // npm package to make these properties immutable. You don't have to use
    // all of these, just pick the ones you need.
    currentSlide: state.currentSlide,
    disableAnimation: state.disableAnimation,
    hasMasterSpinner: state.hasMasterSpinner,
    imageErrorCount: state.imageErrorCount,
    imageSuccessCount: state.imageSuccessCount,
    lockOnWindowScroll: state.lockOnWindowScroll,
    masterSpinnerThreshold: state.masterSpinnerThreshold,
    naturalSlideHeight: state.naturalSlideHeight,
    naturalSlideWidth: state.naturalSlideWidth,
    orientation: state.orientation,
    slideSize: state.slideSize,
    slideTraySize: state.slideTraySize,
    step: state.step,
    dragStep: state.dragStep,
    totalSlides: state.totalSlides,
    touchEnabled: state.touchEnabled,
    dragEnabled: state.dragEnabled,
    visibleSlides: state.visibleSlides,
  }));

Any component wrapped with WithStore will also receive a prop called carouselStore which contains the method setStoreState which you can use to "safely" (use at your own risk) mutate the CarouselProvider's state. There are other methods in carouselStore. Don't use them.

setStoreState: πŸ¦„ 🌈 πŸŽ‚ Use this to mutate any of the properties listed in the WithStore example above. For example, if you want to skip to slide 2 you can put this.props.carouselStore.setStoreState({ currentSlide: 2 }) inside a class method in your component.

More pseudocode.

  import React from 'react';
  import { WithStore } from 'pure-react-carousel';

  class YourComponentHere extends React.Component {
    // ... stuff

    handleClick() {
      this.props.carouselStore.setStoreState({ currentSlide: 2 });
    }
  }

  export default WithStore(YourComponentHere);

Fun fact: you can add any arbitrary values that you want to the CarouselProvider's state. So, if you have several custom components that need to share data, have at it.

masterSpinnerError: πŸ’€ DON'T USE THIS.

masterSpinnerSuccess: ⚠️ DON'T USE THIS.

subscribeMasterSpinner: πŸ’© DON'T USE THIS.

unsubscribeMasterSpinner: πŸ”₯ DON'T USE THIS.

unsubscribeAllMasterSpinner: Don't call this manually unless you have some sort of super-customized carousel. This is called internally once all <Image hasMasterSpinner /> and all <ImageWithZoom hasMasterSpinner /> components are finished loading their images. Calling this directly will force a "success" state and the master spinner (the spinner that covers the entire carousel while loading) will turn off.

Hooks and useContext

If you'd like to consume the context via hooks rather than using the HoC approach described above, the context is exported as CarouselContext.

Note that you will likely need to subscribe/unsubscribe to changes in order to take advantage of the context.

Example:

import React, { useContext, useEffect, useState } from 'react';
import { CarouselContext } from 'pure-react-carousel';

export function MyComponentUsingContext() {
  const carouselContext = useContext(CarouselContext);
  const [currentSlide, setCurrentSlide] = useState(carouselContext.state.currentSlide);
  useEffect(() => {
    function onChange() {
      setCurrentSlide(carouselContext.state.currentSlide);
    }
    carouselContext.subscribe(onChange);
    return () => carouselContext.unsubscribe(onChange);
  }, [carouselContext]);
  return `The current slide is: ${currentSlide}`;
}

TypeScript usage

The current bundled Typescript definitions are mostly complete. Certain edge case could have been not accounted for! Pull requests to improve them are welcome and appreciated.

If you've never contributed to open source before, then you may find this free video course helpful.

In case of provided components, it is pretty straightforward. Simply import them and pass necessary props. At the moment types will not prevent you from using the library incorrectly (for example rendering Slider outside CarouselProvider) therefore please check the documentation if something goes wrong.

WithStore() Higher Order Component

Following the documentation above, only props safe to use are exposed:

interface CarouselState {
  readonly currentSlide: number
  readonly disableAnimation: boolean
  readonly disableKeyboard: boolean
  readonly hasMasterSpinner: boolean
  readonly imageErrorCount: number
  readonly imageSuccessCount: number
  readonly lockOnWindowScroll: boolean
  readonly masterSpinnerThreshold: number
  readonly naturalSlideHeight: number
  readonly naturalSlideWidth: number
  readonly orientation: 'horizontal' | 'vertical'
  readonly slideSize: number
  readonly slideTraySize: number
  readonly step: number
  readonly dragStep: number
  readonly totalSlides: number
  readonly touchEnabled: boolean
  readonly dragEnabled: boolean
  readonly visibleSlides: number
}

export interface CarouselInjectedProps {
  readonly carouselStore: {
    readonly setStoreState: (state: CarouselState) => void
    readonly unsubscribeAllMasterSpinner: () => void
  }
}

Also the first argument which is the component to wrap, needs to be a React.ComponentClass to render properly and therefore stateless component are not possible.

Examples:

  • Both with MapStateToProps and custom props
import {
  CarouselInjectedProps,
  WithStore,
} from 'pure-react-carousel'

interface UpdateCheckProps extends CarouselInjectedProps {
  readonly name: string,
}

interface UpdateCheckCarouselState {
  readonly currentSlide: number,
  readonly disableAnimation: boolean,
}

class InjectedComponent extends Component<
  UpdateCheckProps & UpdateCheckCarouselState
> {
  public render() {
    console.log(this.props)
    return <div>I am a fancy class</div>
  }
}

const DecoratedComponent = WithStore<UpdateCheckProps, UpdateCheckCarouselState>(
  InjectedComponent,
  state => ({
    currentSlide: state.currentSlide,
    disableAnimation: state.disableAnimation,
  }),
)

<CarouselProvider>
  <DecoratedComponent name="NewName" />
</CarouselProvider>
  • No MapStateToProps
interface UpdateCheckProps extends CarouselInjectedProps {
  readonly name: string,
}

class InjectedComponent extends Component<UpdateCheckProps> {
  public render() {
    console.log(this.props)
    return <div>I am a fancy class</div>
  }
}

const DecoratedComponent = WithStore<UpdateCheckProps>(InjectedComponent)

// This will work too, with or without custom props

const DecoratedComponent = WithStore(InjectedComponent)

More Documentation to Come

I promise to add docs for every component. In the meantime, feel free to download and run the demo app. Looking at the code might help you out.

Dev Workflow

  • npm start starts a local development server, opens the dev page with your default browser, and watches for changes via livereload

  • npm run build compiles commonjs and ES modules and places them in the dist directory

  • npm test runs unit and integration tests using Jest + Enzyme. Also does coverage reporting.

  • npm lint runs linting tests using eslint & airbnb linting.

  • npm test:watch same as npm test but it will watch for updates and auto-run tests. Does not do coverage reporting.

Contributors

Thanks goes to these wonderful people (emoji key):


Matthew Toledo

πŸ’» 🎨 πŸ‘€ πŸ€” πŸ“†

Timothy Steele

πŸ’» 🎨 πŸ‘€ πŸ€” πŸ“†

Jedrzej Lewandowski

πŸ’»

Justin Little

πŸ’»

Christoffer Palm

πŸ’»

Dylan Pyle

πŸ’»

Martin Schwier

πŸ’»

Arnaud Ambroselli

πŸ’»

Chung

πŸ’»

Federico Dionisi

πŸ’»

Jakub JastrzΔ™bski

πŸ’»

Jean-Paul Delimat

πŸ’»

Martin

πŸ’»

Per EnstrΓΆm

πŸ’»

Serafino Messina

πŸ’»

Seth Killian

πŸ’»

Tim Chen

πŸ’»

Zoey Zou

πŸ’»

djshapiro

πŸ’»

Brandon Carroll

πŸ’» 🚧

Justin Conner

πŸ’»

StΓ©phane Klein

πŸ“–

Kevyn Bruyere

πŸ’»

Andy Schuler

πŸ’»

Mario Hoyos

πŸ’»

Nick Randall

πŸ‘€

Carson Full

πŸ’»

Kristaps

πŸ’»

Pavel

πŸ’»

Sean LeBlanc

πŸ’»

Sebastian Martha

πŸ’»

Hannes GΓΌdelhΓΆfer

πŸ’»

Zach Levine

πŸ’»

Alec WM

πŸ’»

zhongdeliu

πŸ’»

Niko Savas

πŸ’»

Bret

πŸ’»

Brian B. Canin

πŸ’¬

This project follows the all-contributors specification. Contributions of any kind welcome!

Issues
  • Is it possible to make the first slide appear again after the last slide? (Infinite loop)

    Is it possible to make the first slide appear again after the last slide? (Infinite loop)

    Hello,

    I was wondering if it is possible to have the last slide redirect back to the first slide, so that when a user reaches the last slide on a carousel, they can directly swipe back over to the first slide, without having to back track through all the slides. Right now, a user has to click the "back" navigation button to go back to the first slide, but I am trying to make it so that if they click "next" after the last slide, they are taken back to the first slide.

    Any suggestions are appreciated!

    enhancement help wanted work in progress 
    opened by Pgma14 28
  • Added support for mouse dragging

    Added support for mouse dragging

    Hey,

    I've added support for mouse dragging including some tests and an example that has clickable links that can be clicked while not dragging and will prevent any clicks while dragging.

    I also updated the typings and the readme.

    Did some minor refactoring on the Slider methods so that both mouse dragging and touch dragging use the same logic for setting state and the animations.

    Please let me know if you want me to change anything!

    Best Regards Christoffer

    enhancement work in progress 
    opened by NormySan 19
  • Full height carousel

    Full height carousel

    I had to create a carousel that takes 100% space both vertically and horizontally. Here's the wrapper I came up with:

    import React from 'react';
    import { CarouselProvider } from 'pure-react-carousel';
    import PropTypes from 'prop-types';
    
    class FullContainerCarouselProvider extends React.Component {
      static propTypes = {
        children: PropTypes.node.isRequired
      };
    
      constructor() {
        super();
        this.nodeRef = React.createRef();
        this.state = {
          wrapperHeight: 0,
          wrapperWidth: 0
        };
        this.updateDimensions = this.updateDimensions.bind(this);
      }
    
      updateDimensions() {
        if (!this.nodeRef) return;
        const node = this.nodeRef.current;
        const outerNode = node.parentNode;
    
        this.setState({
          heightProportion: outerNode.clientHeight,
          widthProportion: outerNode.clientWidth
        });
      }
    
      componentDidMount() {
        this.updateDimensions();
        window.addEventListener('resize', this.updateDimensions);
      }
    
      componentWillUnmount() {
        window.removeEventListener('resize', this.updateDimensions);
      }
    
      render() {
    
        return (
          <CarouselProvider
            ref={this.nodeRef}
            naturalSlideWidth={this.state.widthProportion}
            naturalSlideHeight={this.state.heightProportion}
            {...this.props}
          >
            {this.props.children}
          </CarouselProvider>
        );
      }
    }
    
    export default FullContainerCarouselProvider;
    

    The use case is so common (I guess) that it would be better to make naturalSlideWidth/naturalSlideHeight optional. If they are omitted, the carousel would occupy all the outer space available.

    If we will make naturalSlideWidth/naturalSlideHeight optional, there is a catch that should be mentioned in the readme: the containing element must have its dimensions specified.

    opened by mvasin 14
  • Added disableDragStep flag to CarouselProvider

    Added disableDragStep flag to CarouselProvider

    What: Added disableDragStep flag to CarouselProvider, in order to get the functionality of just the horizontal scroll, without any drag steps. As long as drag steps are great for desktop / tablet devices, on mobile a lot of users are used to just scrolling the content horizontally.

    Why: I need that functionality in the project. Furthermore, there was some Issue/Feature request regarding this (when I was first doing the research on it), but cannot find it now.

    How: disableDragStep flag has been introduced on CarouselProvider, held in its state, that has default value of false and is passed to the Slider aswell. Slider uses the flag in computeCurrentSlide, passing it to static slidesMoved invocation. slidesMoved checks the passed value and either acts normally, or calculates proper position by dividing num of pixels dragged (delta) by width / height of the slide.

    Checklist:

    • [ ] Documentation added/updated
    • [x] Typescript definitions updated
    • [ ] Tests added and passing
    • [ ] Ready to be merged

    I am opening the pull request first to see what you guys think. If it's something you would like to see and I get your approval, then I will go further with it and add tests, as well as update the documentation. I've tried digging deep into the implementation and do not see any point where it could break the functionality (unless someone provides dragStep and disableDragStep... it's worth noting that the dragStep will not work in such a combination). All tests pass as usual. However, I might be missing something. Would really appreciate your feedback.

    I am also wondering whether the name of this flag disableDragStep is really proper here, maybe it should be renamed to disableAutoDrag or disableAutoAdjust or something, since it doesn't only make dragStep prop disabled, but it also won't adjust when there is a small drag, not a big drag (see slidesMoved implementation).

    Btw. thank you very much for creating this library! We use it all the time now and it works like a charm :)

    stale 
    opened by dominikbysko 14
  • Closes #269 : make dot children prop optional

    Closes #269 : make dot children prop optional

    What:

    Made children of Dot component optional

    Why:

    See issue #269 - when using Dot directly, there may no need to have any children

    How:

    Changed props and updated docs (hopefully :) )

    Checklist:

    • [x] Documentation added/updated
    • [x] Typescript definitions updated (
    • [x] Tests added and passing
    • [x] Ready to be merged
    released 
    opened by zhongdeliu 12
  • onDrag, onTouch callback

    onDrag, onTouch callback

    Slider should provide callbacks when being dragged or touched. Or maybe even better a onSlideChanged callback that is triggered by any user input.

    Currently only the buttons provide callbacks.

    stale 
    opened by ohlr 12
  • blue outline on slides

    blue outline on slides

    It looks like when you set dragEnabled={false} there is a blue outline when you toggle to the next page. I assume this is an accessibility feature but is there a way to get rid of this?

    opened by moontaekim 12
  • Feature: Infinite

    Feature: Infinite

    Hi there.

    Love the implementation and declarative style of this library. The only thing keeping my company and I from using this lib is that its missing the infinite option (see https://react-slick.neostack.com/). The feature is essentially: if we're currently on the last slide and the user hits the next button, go to the first set of slides. if we're on the first set of slides and the user hits the back button, go to the last set of slides. All around, it is a better user experience than completely disabling the next/back buttons.

    Example:

    Aug-29-2019 08-55-13

    I've started to implement the feature as you can see above, and it seems like it'll take minimum effort to add the feature. Looking for your thoughts on adding this feature before I go any further to support edge cases (e.g. where the total number of slides is not divisible by the visible slides) and add tests.

    EDIT:

    This is an optional prop. Without passing the prop, you get the original functionality.

    fixed 
    opened by jdconner 11
  • Prevent swiping while scrolling vertically

    Prevent swiping while scrolling vertically

    Hey guys!

    Thank you for this carousel, it's exactly what I was looking for. However we experienced a undesirable behaviour on mobile: while scrolling vertically the carousel is swiping.

    This is a fix for this bug. Happy to help.

    Cheers

    enhancement 
    opened by axeldelafosse 11
  • Callback When Dragging/Swiping Slides?

    Callback When Dragging/Swiping Slides?

    I can't seem to figure out how to notify the Dots (thumbnails) that the slide has changed when the user swipes/drags to go forward or backward through the slides.

    I have a selected state css style for the dots when the corresponding slide is "active". It works just fine when using buttons or clicking on the dots themselves, but I don't know how to notify the dot(s) of a slide change when the user swipes/drags. Anyone have a solution for this? Ideally there would be an onChange event on the CarouselProvider or the Slider.

    wontfix 
    opened by ErnestGrey 11
  • Having DevTools open affects transition calculation

    Having DevTools open affects transition calculation

    • pure-react-carousel version: 1.27.8
    • react version: 17.0.2
    • browser used: chrome
    • node version: 14.18.1

    Relevant code or config:

    import { CarouselProvider, Slider, Slide, ButtonBack, ButtonNext } from 'pure-react-carousel'
    import 'pure-react-carousel/dist/react-carousel.es.css'
    
    const IMG = ['', '', '']
    
    export default () => {
    	return (
    		<>
    			<CarouselProvider naturalSlideWidth={100} naturalSlideHeight={125} totalSlides={IMG.length}>
    				<Slider className="slider">
    					{IMG.map((img, i) => (
    						<Slide index={i} key={i}>
    							<div style={{ background: i % 2 ? 'yellow' : 'red' }}>{i}</div>
    						</Slide>
    					))}
    				</Slider>
    				<ButtonBack>Back</ButtonBack>
    				<ButtonNext>Next</ButtonNext>
    			</CarouselProvider>
    		</>
    	)
    }
    

    What you did:

    1. Open DevTools (Ctrl+Shift+C in Windows Google Chrome)
    2. Press <ButtonNext> UI button
    3. Then drag a slide with a cursor to the right multiple times (dragging it multiple times fast does it almost all the time)

    What happened:

    When having DevTools open the Slides transition offset would often become wrong which stopped aligning further slides correctly.

    Reproduction:

    Copy pasting component from Relevant code or config section would reproduce it.

    stale 
    opened by HaruMiyaGi 1
  • Example site is stuck on loading

    Example site is stuck on loading

    image

    stale 
    opened by the-wrong-guy 1
  • Add image loading error handling capabilities to ImageWithZoom

    Add image loading error handling capabilities to ImageWithZoom

    Describe the feature you'd like: ImageZoom adds the zoom functionality, but removes the ability to handle loading errors. Then as users we have to choose between handling loading errors but not having zoom, or having zoom but not being able to handle loading errors.

    The need to handle loading errors is still present when using the zoom functionality, to give visual feedback to the end user as to what happened.

    Suggested implementation:

    Add the renderError and onError props so they work in the same way.

    Describe alternatives you've considered:

    None that I could imagine

    Teachability, Documentation, Adoption, Migration Strategy:

    The docs don't have a props table for ImageWithZoom, so it could be added with the error handling props included, the same as the ones for Image.

    stale 
    opened by carles-codony-manomano 1
  • onWheel movement of the Slider

    onWheel movement of the Slider

    Slide using onWheel

    I would like to be able to capture the onWheel function from mouse/touchpad allowing for the Slider to scroll left on mouseWheelUp (deltaY negative), and right for mouseWheelDown (deltaY possitive)

    stale 
    opened by silberg 1
  • Getting the carousel to keep scrolling in one direction

    Getting the carousel to keep scrolling in one direction

    Issues on GitHub are intended to be related to problems and feature requests so we ask you not use issues to ask for support.


    ❓ Pure React Carousel Resources

    • Tutorial - https://github.com/express-labs/pure-react-carousel#-tutorial
    • Examples - https://express-labs.github.io/pure-react-carousel/
    • Stack Overflow - https://stackoverflow.com/questions/tagged/pure-react-carousel

    ISSUES WHICH ARE QUESTIONS WILL BE CLOSED

    Hi guys, thanks for the great library.

    I'm working on a project using the carousel to scroll through large background images. I really want the carousel to keep scrolling right when it reaches the end of the slides, but when it reaches the final slide, it scrolls left back through them really quickly. Is this possible? I tried the infinite prop, but that didn't do it.

    Alternatively, It'd be fine if I could get the carousel to change directions when it reaches the end. I tried adding some state and a function that could change the direction but the CarouselProvider wasn't listening to it :S

    stale 
    opened by BruceJi7 1
  • feat: add disableSnap on carrousel

    feat: add disableSnap on carrousel

    What: Enable user drag the slider without snap to the next/previous slide

    Why: Fixes the following issues: https://github.com/express-labs/pure-react-carousel/issues/86, https://github.com/express-labs/pure-react-carousel/pull/289, https://github.com/express-labs/pure-react-carousel/issues/362

    How: The changes was made on <Slider />, mainly on methods fakeOnDragMove and fakeOnDragEnd, added a two intermediates states, one that will be updated on OnDragMove the and another that will OnDragEnd, that way we can sum and iterate the first without lost the previous state before drag. This implementation disables by default isPlaying, what makes sense, once the user chose per disable snap he wants to user scroll as he wants. To use disableOnSnap is necessary to add the prop dragStep={0}

    https://user-images.githubusercontent.com/17225358/136815461-01612500-4833-4dac-b5c8-1a0328e96119.mp4

    Checklist:

    • [ ] (N/A) Documentation added/updated
    • [ ] (N/A) Typescript definitions updated
    • [ ] (N/A) Tests added and passing
    • [x] Ready to be merged
    stale 
    opened by antoniel 2
  • Slider OnClick is not being prevented on Carousel movement

    Slider OnClick is not being prevented on Carousel movement

    • pure-react-carousel version: 1.27.6
    • react version: 17.0.2
    • node version: v10.16.1

    Relevant code or config:

    <Slider> > handleOnClickCapture(ev) line #286 Screen Shot 2021-08-05 at 1 10 43 PM

    const your = code => 'here';
    

    What you did:

    We are attempting to prevent clicks of the carousel slider when the carousel is in movement.

    What happened:

    The onClick event is still triggered on drag events

    Reproduction:

    Suggested solution:

    Remove the line #293 from <Slider /> =

    Can you help us fix this issue by submitting a pull request?

    stale 
    opened by captnstarburst 4
  • Accessibility | AXE and Lighthouse shows issues

    Accessibility | AXE and Lighthouse shows issues

    Getting accessibility issues in lighthouse and AXE in chrome dev tools.

    • pure-react-carousel version: 1.27.6
    • react version: 16.10.0
    • browser used: Chrome
    • node version: v10.14.1

    Steps to Reproduce:

    Run lighthouse or axe in chrome dev tools. I have a button inside slide and it's not read and throws this error AXE tool.

    Issues: 1.Nested interactive controls are not announced by screen readers

    help wanted under investigation 
    opened by Pragya-singh-14 1
  • Custom Slide Timings Reopening #218

    Custom Slide Timings Reopening #218

    What:

    There is now the option to create a list of timings for the duration of each slide.

    Why:

    They allow users more freedom and assist with content such as slideshows where there is varying amounts of content per slide.

    How:

    There is a new Slider property called Interval list that allows the user to input that in addition to intervals and it will automatically apply the list instead.

    Checklist:

    • [x] Documentation added/updated
    • [x] Typescript definitions updated
    • [x] Tests added and passing
    • [x] Ready to be merged
    work in progress 
    opened by Bracciata 5
  • Option to remove dependency on global CSS

    Option to remove dependency on global CSS

    I'm importing this package into another for my app where I use CSS modules and will wrap this carousel in another component. I'd like to not use global CSS, so my consuming Next.JS app doesn't take a direct dependency on this module.

    Currently I can take the supplied CSS and bring that into my own CSS module and pass all the required classes into the components using className props. However the carousel will still output all the original CSS class references - this doesn't appear to break anything but does lead to references to classes that don't exist. Ideally I could use a parameter to disable the output of the default classes - a way of telling the carousel that I'll take ownership for the CSS.

    Would that be possible? Is there a problem this will cause, something that is in the global CSS that I cannot currently provide via my own set of classes?

    enhancement under investigation 
    opened by AlexW-Valtech 0
Releases(v1.27.8)
Owner
Express Labs
Express Labs
Items Carousel Built with react-motion and styled-components

react-items-carousel Installation $ npm install react-items-carousel --save Demos Example import React, { useState } from 'react'; import ItemsCarous

Kareem Elbahrawy 187 Jan 4, 2022
Flipping Cards for your React Projects. React-Flippy allows you to create flipper event with your content.

React-Flippy React-Flippy allows you to create flipping cards in React projects. It can be used as controlled or uncontrolled component. Live Demo How

Berkay AydΔ±n 295 Jan 7, 2022
A lightweight React component that utilizes the power of CSS animations to create silky smooth marquees.

React Fast Marquee React Fast Marquee is a lightweight React component that utilizes the power of CSS animations to create silky smooth marquees. Demo

null 210 Jan 18, 2022
Rerousel is the simplest and the lightest infinite carousel package made for React.

Rerousel is the simplest and the lightest infinite carousel package made for React. Simple Set up your carousel in the blink of an eye. You only have

Aexol 96 Jan 8, 2022
Touch and drag slider carousel component for React

react-touch-drag-slider Touch and drag slider carousel component for React Touch friendly on mobile Responsive to viewport resizing Supports mouse dra

Will Adams 164 Jan 13, 2022
React simple infinite carousel with lazy loading and responsive support

react-leaf-carousel React simple infinite carousel with lazy loading and responsive support. Installation npm install react-leaf-carousel Example impo

Leaf Grow 89 Dec 31, 2021
A flexible and responsive carousel component for react

react-elastic-carousel A flexible and responsive carousel component for react Why do we need yet another carousel component Element resize support (tr

Sagiv ben giat 257 Jan 18, 2022
🎠 ♻️ Everyday 30 million people experience. It's reliable, flexible and extendable carousel.

?? ♻️ Everyday 30 million people experience. It's reliable, flexible and extendable carousel.

NAVER 1.9k Jan 12, 2022
A lightweight production-ready Carousel that rocks supports multiple items and server-side rendering with no dependency. Bundle size 2kb.

react-multi-carousel ?? Production-ready, lightweight fully customizable React carousel component that rocks supports multiple items and SSR(Server-si

yi 799 Jan 15, 2022
Mobile-friendly gallery carousel 🎠🎠🎠 with batteries included (touch, mouse emulation, lazy loading, thumbnails, fullscreen, RTL, keyboard navigation and customisations).

react-gallery-carousel Mobile-friendly Carousel with batteries included (supporting touch, mouse emulation, lazy loading, thumbnails, fullscreen, RTL,

Yifan Ai 169 Jan 10, 2022
React.js Responsive Carousel (with Swipe)

React Responsive Carousel Powerful, lightweight and fully customizable carousel component for React apps. Important I don't have any time available to

Leandro Lemos 2k Jan 20, 2022
React carousel component

react-slick Carousel component built with React. It is a react port of slick carousel Documentation Installation npm npm install react-slick --save ya

Kiran Abburi 10.1k Jan 21, 2022
Custom React hook for building an accessible Carousel component.

Build an accessible React Carousel component with usePony

JoΓ£o Viana 6 Aug 5, 2021
Lightweight carousel component for react

Lightweight carousel component for react

Trendyol Open Source 135 Jan 15, 2022
A react carousel/slider like component for sequentially displaying slides or sets of slides

react-whirligig demo repo A carousel/slider-like component for sequentially displaying slides or sets of slides. Basic Usage const Slider = ({ slideIn

Jane 22 Nov 15, 2021
Minimal carousel component for React.

re-carousel Minimal carousel component for React. demo: https://amio.github.io/re-carousel/ Usage import Carousel from 're-carousel' then: <Carousel a

Amio Jin 245 Dec 29, 2021
React part of bootstrap carousel

react-bootstrap-carousel This project support carousel components built with React. Install npm i --save react-bootstrap-carousel Getting Started impo

CT Hung 11 Oct 18, 2021
Simple multifunctional controlled react.js carousel component. Touch enabled, responsive, infinite, with autoplay support. (with ssr support)

react-simply-carousel Simple && small controlled React.js carousel component (touch enabled, infnite and responsive) Table of contents Installation Us

Vadym Shymko 37 Jan 5, 2022