npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@bam.tech/react-native-snap-carousel

v3.2.3

Published

Swiper component for React Native with previews, snapping effect, parallax images, performant handling of huge numbers of items, and RTL support. Compatible with Android & iOS.

Downloads

190

Readme

react-native-snap-carousel

Swiper component for React Native featuring previews, snapping effect, parallax images, performant handling of huge numbers of items, and RTL support. Compatible with Android & iOS.

platforms npm github release github issues github closed issues

Table of contents

  1. Showcase
  2. Usage
  3. Migration from version 2.x
  4. Props
  5. Methods
  6. Getters
  7. ParallaxImage component
  8. Pagination component
  9. Example
  10. Tips and tricks
  11. Known issues
  12. TODO
  13. Credits

Showcase

Archriss' "Ville d'Aix-en-Provence" app

The React Native version of this app (6.0.0) is going to be available on Android and iOS by mid-september (the current one is Cordova-powered). It uses version 3.1.0 of the plugin, with FlatList's implementation and parallax images.

react-native-snap-carousel archriss aix react-native-snap-carousel archriss aix

Archriss' showcase app

You can try the app live on Android and iOS. It currently uses version 1.4.0 of the plugin. Be aware that sliders' layouts will break on RTL devices since support was added in version 2.1.0 (see #38).

react-native-snap-carousel react-native-snap-carousel react-native-snap-carousel

Please note that we do not plan on Open-Sourcing the code of our showcase app. Still, we've put together an example for you to play with, and you can find some insight about our map implementation in this comment. The folks at codedaily.io have put together a great tutorial about implementing a similar feature. Go check it out!

Usage

$ npm install --save react-native-snap-carousel
import Carousel from 'react-native-snap-carousel';

export class MyCarousel extends Component {

    _renderItem ({item, index}) {
        return (
            <View style={styles.slide}>
                <Text style={styles.title}>{ item.title }</Text>
            </View>
        );
    }

    render () {
        return (
            <Carousel
              ref={(c) => { this._carousel = c; }}
              data={this.state.entries}
              renderItem={this._renderItem}
              sliderWidth={sliderWidth}
              itemWidth={itemWidth}
            />
        );
    }
}

Migration from version 2.x

Slides are no longer appended as direct children of the component since the plugin is now based on FlatList instead of ScrollView. There are two new props that takes care of their rendering: data and renderItem (both are inherited from FlatList).

:warning: Since FlatList was introduced in React Native 0.43.x, you need to use version 2.4.0 of the plugin if you're currently using an older release of RN. Please note that we won't support this older version of the plugin. Also, make sure to check the relevant documentation.

If you were already looping throught an array of data to populate the carousel, the migration is pretty straightforward. Just pass your slides' data to the data prop, convert your slides' getter to a function and pass it to the renderItem prop: you're good to go!

From

    get slides () {
        return this.state.entries.map((entry, index) => {
            return (
                <View key={`entry-${index}`} style={styles.slide}>
                    <Text style={styles.title}>{ entry.title }</Text>
                </View>
            );
        });
    }

    render () {
        return (
            <Carousel
              sliderWidth={sliderWidth}
              itemWidth={itemWidth}
            >
                { this.slides }
            </Carousel>
        );
    }

To

    _renderItem ({item, index}) {
        return (
            <View style={styles.slide}>
                <Text style={styles.title}>{ item.title }</Text>
            </View>
        );
    }

    render () {
        return (
            <Carousel
              data={this.state.entries}
              renderItem={this._renderItem}
              sliderWidth={sliderWidth}
              itemWidth={itemWidth}
            />
        );
    }

Note that the key prop is no longer needed for carousel's items. If you want to provide a custom key, you should pass your own keyExtractor to the <Carousel />.

If you were previously appending random types of children, you will need to rely on a specific bit of data to return the proper element from your renderItem function.

Example

    _renderItem ({item, index}) {
        if (item.type === 'text') {
            return <Text style={styles.textSlide} />;
        } else if (item.type === 'image') {
            return <Image style={styles.imageSlide} />;
        } else {
            return <View style={styles.viewSlide} />;
        }
    }

Props

Required

Prop | Description | Type | Default ------ | ------ | ------ | ------ data | Array of items to loop on | Array | Required renderItem | Takes an item from data and renders it into the list. The function receives one argument {item, index} (see Usage) and must return a React element. | Function | Required itemWidth | Width in pixels of carousel's items, must be the same for all of them | Number | Required for horizontal carousel sliderWidth | Width in pixels of the carousel itself | Number | Required for horizontal carousel itemHeight | Height in pixels of carousel's items, must be the same for all of them | Number | Required for vertical carousel sliderHeight | Height in pixels of the carousel itself | Number | Required for vertical carousel

Behavior

Prop | Description | Type | Default ------ | ------ | ------ | ------ activeSlideOffset | From slider's center, minimum slide distance to be scrolled before being set to active. | Number | 20 apparitionDelay | FlatList's init is a real mess, with lots of unneeded flickers and slides movement. This prop controls the delay during which the carousel will be hidden when mounted. | Number | 250 callbackOffsetMargin | Scroll events might not be triggered often enough to get a precise measure and, therefore, to provide a reliable callback. This usually is an Android issue, which might be linked to the version of React Native you're using (see "Unreliable callbacks"). To work around this, you can define a small margin that will increase the "sweet spot"'s width. The default value should cover most cases, but you will want to increase it if you experience missed callbacks. | Number | 5 enableMomentum | See momentum | Boolean | false enableSnap | If enabled, releasing the touch will scroll to the center of the nearest/active item | Boolean | true firstItem | Index of the first item to display | Number | 0 hasParallaxImages | Whether the carousel contains <ParallaxImage /> components or not. Required for specific data to be passed to children. | Boolean | false shouldOptimizeUpdates | Whether to implement a shouldComponentUpdate strategy to minimize updates | Boolean | true swipeThreshold | Delta x when swiping to trigger the snap | Number | 20 vertical | Layout slides vertically instead of horizontally | Boolean | false

Autoplay

Prop | Description | Type | Default ------ | ------ | ------ | ------ autoplay | Trigger autoplay on mount. Warning: this prop cannot be changed dynamically. | Boolean | false autoplayDelay | Delay before enabling autoplay on startup & after releasing the touch | Number | 5000 autoplayInterval | Delay in ms until navigating to the next item | Number | 3000

Style and animation

Prop | Description | Type | Default ------ | ------ | ------ | ------ activeSlideAlignment | Determine active slide's alignment relative to the carousel. Possible values are: 'start', 'center' and 'end'. | String | 'center' containerCustomStyle | Optional styles for Scrollview's global wrapper | View Style Object | {} contentContainerCustomStyle | Optional styles for Scrollview's items container | View Style Object | {} inactiveSlideOpacity | Value of the opacity effect applied to inactive slides | Number | 0.7 inactiveSlideScale | Value of the 'scale' transform applied to inactive slides | Number | 0.9 slideStyle | Optional style for each item's container (the one whose scale and opacity are animated) | Animated View Style Object | {}

Callbacks

Prop | Description | Type | Default ------ | ------ | ------ | ------ onLayout(event) | Exposed View callback; invoked on mount and layout changes | Function | undefined onScroll(event) | Exposed ScrollView callback; fired while scrolling | Function | undefined onSnapToItem(slideIndex) | Callback fired when navigating to an item | Function | undefined

Inherited props

The component is built on top of the FlatList component, meaning it inherits from FlatList, VirtualizedList, and ScrollView.

You can use almost all props from this three components, but some of them can't be overriden because it would mess with our implementation's logic.

Here are a few useful props regarding carousel's style and "feeling": scrollEnabled (if you want to disable user scrolling while still being able to use Carousel's methods), showsHorizontalScrollIndicator, overScrollMode (android), bounces (ios), decelerationRate (ios), scrollEventThrottle (ios).

And here are some useful ones for performance optimizations and rendering: initialNumToRender, maxToRenderPerBatch, windowSize, updateCellsBatchingPeriod, extraData, removeClippedSubviews (the latter may have bugs, as stated in RN's doc). The first three are already implemented with default parameters, but you can override them if they don't suit your needs.

Methods

Reference to the component

In order to use the following methods, you need to create a reference to the carousel's instance. There are two ways of doing it.

ref as a callback attribute (recommended)

<Carousel
  // other props
  ref={(c) => { this._carousel = c; }}
/>

// methods can then be called this way
onPress={() => { this._carousel.snapToNext(); }}

ref as a string attribute (legacy)

<Carousel
  // other props
  ref={'carousel'}
/>

// methods can then be called this way
onPress={() => { this.refs.carousel.snapToNext(); }}

Available methods

Method | Description ------ | ------ startAutoplay (instantly = false) | Start the autoplay manually stopAutoplay () | Stop the autoplay manually snapToItem (index, animated = true) | Snap to an item manually snapToNext (animated = true) | Snap to next item manually snapToPrev (animated = true) | Snap to previous item manually

Getters

You need a reference to the carousel's instance (see above if needed).

Property | Description ------ | ------ currentIndex | Current active item (int, starts at 0) currentScrollPosition | Underlying ScrollView's current content offset (int, starts at 0 if activeSlideAlignment is set to start, negative value otherwise)

ParallaxImage component

Version 3.0.0 introduced a <ParallaxImage /> component, an image component aware of carousel's current scroll position and therefore able to display a nice parallax effect.

You can find the documentation for this component here.

react-native-snap-carousel parallax image

Pagination component

Starting with version 2.4.0, a customizable <Pagination /> component has been added. This is how it looks like with its default configuration:

You can find the documentation for this component here.

react-native-snap-carousel pagination

Example

You can find the following example in the /example folder.

react-native-snap-carousel

Tips and tricks

Momentum

Since version 1.5.0, the snapping effect can be based on momentum (by setting enableMomentum to true) instead of when you're releasing your finger. It means that the component will wait until the ScrollView isn't moving anymore to snap.

By default, the inertia isn't too high on Android. However, we had to tweak the default iOS value a bit to make sure the snapping isn't delayed for too long. You can adjust this value to your needs thanks to this prop.

If momentum is disabled (default behavior), make sure to play with prop scrollEndDragDebounceValue since it can help achieving a better snap feeling.

We recommend setting enableMomentum to false (default) and decelerationRate to 'fast' when you are displaying only one main slide (as in the showcase above), and to use true and 0.9 otherwise.

Margin between slides

If you need some extra horizontal margin between slides (besides the one resulting from the scale effect), you should add it as paddingHorizontal on slide's container.

:warning: The value of itemWidth must include this extra margin.

const horizontalMargin = 20;
const slideWidth = 280;

const sliderWidth = Dimensions.get('window').width;
const itemWidth = slideWidth + horizontalMargin * 2;
const itemHeight = 200;

const styles = Stylesheet.create({
    slide: {
        width: itemWidth,
        height: itemHeight
        // other styles for your item's container
    }
};
<Carousel sliderWidth={sliderWidth} itemWidth={itemWidth} />

Carousel's stretched height

Since <Carousel /> is, ultimately, based on <ScrollView />, it inherits its default styles and particularly { flexGrow: 1 }. This means that, by default, the carousel container will stretch to fill up all available space.

If this is not what you're after, you can prevent this behavior by passing { flexGrow: 0 } to prop containerCustomStyle.

Alternatively, you can either use this prop to pass a custom height to the container, or wrap the carousel in a <View /> with a fixed height.

Understanding styles

Here is a screenshot that should help you understand how each of the above variables is used.

react-native-snap-carousel info

Handling device rotation

Since version 2.2.0, slides will re-center properly if you update slider and/or items' dimensions when onLayout is fired.

Here is an example of a working implementation (thanks @andrewpope):

constructor(props) {
    super(props);
    this.state = {
        viewport: {
            width: Dimensions.get('window').width,
            height: Dimensions.get('window').height
        }
    };
}

render() {
    return (
        <View
            onLayout={() => {
                this.setState({
                    viewport: {
                        width: Dimensions.get('window').width,
                        height: Dimensions.get('window').height
                    }
                });
            }}
        >
            <Carousel
                ref={c => { this.carousel = c; } }
                sliderWidth={this.state.viewport.width}
                itemWidth={this.state.viewport.width}
                ...
            />
        </View>
    );
}

Fullscreen slides

While the plugin hasn't been designed with this use case in mind, you can easily implement fullscreen slides. The following code can serve as a good starting point.

const { width: viewportWidth, height: viewportHeight } = Dimensions.get('window');

export class MyCarousel extends Component {

    _renderItem ({item, index}) {
        return (
            <View style={{ height: viewportHeight }} /> // or { flex: 1 } for responsive height
        );
    }

    render () {
        return (
            <Carousel
              data={this.state.entries}
              renderItem={this._renderItem}
              sliderWidth={viewportWidth}
              itemWidth={viewportWidth}
              slideStyle={{ width: viewportWidth }}
              inactiveSlideOpacity={1}
              inactiveSlideScale={1}
            />
        );
    }
}

This plugin can also prove useful.

Native-powered animations

Scroll events have been moved to the native thread in order to prevent the tiny lag associated with React Native's JavaScript bridge. This is really useful when displaying a transform and/or opacity animation that needs to follow carousel's scroll position closely. You can find more info in this post from Facebook.

Known issues

React Native version

:warning: RN 0.43.x is the minimum required to use versions >= 3.0.0 of the plugin. If you're using an older release of React Native, you are stuck with version 2.4.0. Please note that we won't support this older version of the plugin. Also, make sure to check the relevant documentation.

Bear in mind that we follow RN evolutions closely, which means newer versions of the plugin might break when used in conjunction with a version of RN that is not the latest stable one.

Android performance

:warning: Make sure to test carousel's performance and behavior without JS Dev Mode enabled, ideally with a production build..

It can take user experience from "crappy and sluggish" to "pretty good" - it's Android though, so nothing like "perfect" or "incredibly smooth"...

FlatList and ScrollView's limitations

Note that this plugin is built on top of React Native's FlatList which, in turn, is based on ScrollView. Unfortunately, its implementation shows flaws that affect the plugin, the main ones being the following:

  • there is no scrollEnd event
  • scrollTo method doesn't accept any callback
  • Android's scrollTo animation is quite brutal.

On top of that, FlatList has its own set of bugs and buggy behaviors.

We're trying to work around these issues, but the result is not always as smooth as we'd want it to be. Keep that in mind and go spam React Native's Feature Request ;-)

Unreliable callbacks

When enableMomentum is disabled (default behavior), providing a reliable callback is really tricky since no scrollEnd event has been exposed yet for the ScrollView component. We can only rely on the scrollEndDrag event, which comes with a huge bunch of issues. See #34 for more information.

Version 2.3.0 tackled these issues with all sorts of flags and hacks. But you could still be facing the following one: when you build a debug version of your app without enabling JS remote debugging, timers may desynchronize and cause a complete callback mess. Try to either enable remote debugging or build a production version of your app, and everything should get back to normal.

Callback handling has been completely revamped in version 3.2.0, in a less hacky and more reliable way. There is one issue though: callbacks now rely on scroll events. Usually, this is not a problem since the plugin features a native-powered scroll. But there has been a regression in React Native 0.46.x, that has been fixed in version 0.48.2.

If you're using an in-between version, you're in for some trouble since events won't be fired frequently enough (particularly on Android). We've added a prop callbackOffsetMargin to help with this situation.

Error with Jest

You might encounter the following error when using the plugin in conjonction with Jest: TypeError: Cannot read property 'style' of undefined at Object.<anonymous>.

As you can see here, this is because React Native mocks ScrollView for you when you write unit tests with Jest.

The easiest workaround is to add jest.unmock('ScrollView') before importing the component in your test file (thanks @hoangnm for the tip!).

RTL support (experimental)

Since version 2.1.0, the plugin is compatible with RTL layouts. Our implementation relies on miscellaneous hacks that work around a React Native bug with horizontal ScrollView.

As such, this feature should be considered experimental since it might break with newer versions of React Native.

TODO

  • [ ] Implement a custom PanResponder for better control over carousel's callbacks and overall feeling
  • [ ] Implement 'loop' mode
  • [ ] Handle changing major props on-the-fly
  • [ ] Handle autoplay properly when updating children's length
  • [x] Add parallax image component
  • [x] Base the plugin on FlatList instead of ScrollView
  • [x] Add alignment option
  • [x] Add pagination component
  • [x] Add vertical implementation
  • [x] Handle device orientation event (see [this note] (https://github.com/archriss/react-native-snap-carousel#handling-device-rotation))
  • [x] Add RTL support
  • [x] Improve momemtum handling
  • [x] Improve snap on Android
  • [x] Handle passing 1 item only
  • [x] Fix centering

Credits

Written by Maxime Bertonnier (Exilz) and Benoît Delmaire (bd-arc) at Archriss.