|
| 1 | +# React Native Hero Carousel |
| 2 | + |
| 3 | +A highly customizable, performant carousel component for React Native with advanced animations, auto-scrolling capabilities, and infinite scrolling support. Built with React Native Reanimated for smooth, native-level performance. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +✨ **Auto-scrolling** with customizable intervals |
| 8 | +🔄 **Infinite scrolling** with seamless transitions |
| 9 | +🎬 **Video support** with play/pause controls |
| 10 | +⏱️ **Timer-based pagination** with visual progress indicators |
| 11 | +🎯 **Advanced animations** using React Native Reanimated |
| 12 | +📱 **Gesture-friendly** with swipe navigation |
| 13 | +🎨 **Highly customizable** with interpolation utilities |
| 14 | +⚡ **Performance optimized** with worklet functions |
| 15 | + |
| 16 | +## Installation |
| 17 | + |
| 18 | +```bash |
| 19 | +npm install @strv/react-native-hero-carousel |
| 20 | +# or |
| 21 | +yarn add @strv/react-native-hero-carousel |
| 22 | +# or |
| 23 | +pnpm add @strv/react-native-hero-carousel |
| 24 | +``` |
| 25 | + |
| 26 | +### Peer Dependencies |
| 27 | + |
| 28 | +This library requires the following peer dependencies: |
| 29 | + |
| 30 | +```bash |
| 31 | +npm install react-native-reanimated react-native-gesture-handler |
| 32 | +``` |
| 33 | + |
| 34 | +Make sure to follow the [React Native Reanimated installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started) for your platform. |
| 35 | + |
| 36 | +## Quick Start |
| 37 | + |
| 38 | +```tsx |
| 39 | +import React from 'react' |
| 40 | +import { View, Text, StyleSheet } from 'react-native' |
| 41 | +import { HeroCarousel, CarouselContextProvider } from '@strv/react-native-hero-carousel' |
| 42 | + |
| 43 | +const slides = [ |
| 44 | + { id: 1, title: 'Slide 1', color: '#FF6B6B' }, |
| 45 | + { id: 2, title: 'Slide 2', color: '#4ECDC4' }, |
| 46 | + { id: 3, title: 'Slide 3', color: '#45B7D1' }, |
| 47 | + { id: 4, title: 'Slide 4', color: '#96CEB4' }, |
| 48 | +] |
| 49 | + |
| 50 | +const Slide = ({ title, color }: { title: string; color: string }) => ( |
| 51 | + <View style={[styles.slide, { backgroundColor: color }]}> |
| 52 | + <Text style={styles.title}>{title}</Text> |
| 53 | + </View> |
| 54 | +) |
| 55 | + |
| 56 | +export default function BasicCarousel() { |
| 57 | + return ( |
| 58 | + <CarouselContextProvider> |
| 59 | + <View style={styles.container}> |
| 60 | + <HeroCarousel> |
| 61 | + {slides.map((slide) => ( |
| 62 | + <Slide key={slide.id} title={slide.title} color={slide.color} /> |
| 63 | + ))} |
| 64 | + </HeroCarousel> |
| 65 | + </View> |
| 66 | + </CarouselContextProvider> |
| 67 | + ) |
| 68 | +} |
| 69 | + |
| 70 | +const styles = StyleSheet.create({ |
| 71 | + container: { |
| 72 | + flex: 1, |
| 73 | + }, |
| 74 | + slide: { |
| 75 | + flex: 1, |
| 76 | + justifyContent: 'center', |
| 77 | + alignItems: 'center', |
| 78 | + }, |
| 79 | + title: { |
| 80 | + fontSize: 24, |
| 81 | + fontWeight: 'bold', |
| 82 | + color: 'white', |
| 83 | + }, |
| 84 | +}) |
| 85 | +``` |
| 86 | + |
| 87 | +## API Reference |
| 88 | + |
| 89 | +### Components |
| 90 | + |
| 91 | +#### `CarouselContextProvider` |
| 92 | + |
| 93 | +The context provider that must wrap your carousel components. |
| 94 | + |
| 95 | +```tsx |
| 96 | +<CarouselContextProvider |
| 97 | + defaultScrollValue={1} // Initial scroll position (default: 1) |
| 98 | + slideWidth={screenWidth} // Width of each slide (default: screen width) |
| 99 | +> |
| 100 | + {children} |
| 101 | +</CarouselContextProvider> |
| 102 | +``` |
| 103 | + |
| 104 | +#### `HeroCarousel` |
| 105 | + |
| 106 | +The main carousel component with auto-scrolling functionality. |
| 107 | + |
| 108 | +```tsx |
| 109 | +<HeroCarousel |
| 110 | + interval={3000} // Auto-scroll interval in ms |
| 111 | + disableAutoScroll={false} // Disable auto-scrolling |
| 112 | + goToPageAnimation={(to, duration) => withTiming(to, { duration })} // Custom page transition animation |
| 113 | +> |
| 114 | + {children} |
| 115 | +</HeroCarousel> |
| 116 | +``` |
| 117 | + |
| 118 | +**Props:** |
| 119 | + |
| 120 | +| Prop | Type | Default | Description | |
| 121 | +| ------------------- | --------------------------------------- | -------- | ----------------------------------------------------------------------------------- | --- | |
| 122 | +| `interval` | `number \| ((index: number) => number)` | `3000` | Auto-scroll interval in milliseconds, or function returning interval for each slide | |
| 123 | +| `disableAutoScroll` | `boolean` | `false` | Disable automatic scrolling | | |
| 124 | +| `children` | `React.ReactNode[]` | Required | Array of slide components | |
| 125 | + |
| 126 | +### Hooks |
| 127 | + |
| 128 | +#### `useCarouselContext()` |
| 129 | + |
| 130 | +Access the carousel's shared state and controls. |
| 131 | + |
| 132 | +```tsx |
| 133 | +const { scrollValue, timeoutValue, slideWidth, userInteracted, setUserInteracted } = |
| 134 | + useCarouselContext() |
| 135 | +``` |
| 136 | + |
| 137 | +**Returns:** |
| 138 | + |
| 139 | +- `scrollValue`: Animated value representing current scroll position |
| 140 | +- `timeoutValue`: Animated value for timer progress (0-1) |
| 141 | +- `slideWidth`: Width of slides |
| 142 | +- `userInteracted`: Boolean indicating if user has interacted with carousel |
| 143 | +- `setUserInteracted`: Function to update interaction state |
| 144 | + |
| 145 | +#### `useHeroCarouselSlideIndex()` |
| 146 | + |
| 147 | +Get the current slide information and auto-scroll controls. |
| 148 | + |
| 149 | +```tsx |
| 150 | +const { index, total, runAutoScroll, goToPage } = useAutoCarouselSlideIndex() |
| 151 | +``` |
| 152 | + |
| 153 | +**Returns:** |
| 154 | + |
| 155 | +- `index`: Current slide index |
| 156 | +- `total`: Total number of slides |
| 157 | +- `runAutoScroll`: Function to manually trigger auto-scroll with custom interval |
| 158 | +- `goToPage`: Function to programmatically navigate to a specific slide from another slide |
| 159 | + |
| 160 | +### Utilities |
| 161 | + |
| 162 | +#### `interpolateInsideCarousel()` |
| 163 | + |
| 164 | +Advanced interpolation utility for creating custom slide animations. |
| 165 | + |
| 166 | +```tsx |
| 167 | +import { interpolateInsideCarousel } from '@strv/react-native-hero-carousel' |
| 168 | + |
| 169 | +const animatedStyle = useAnimatedStyle(() => { |
| 170 | + const progress = interpolateInsideCarousel(scrollValue.value, slideIndex, total, { |
| 171 | + valueBefore: 0, // Value for slides before current |
| 172 | + thisValue: 1, // Value for current slide |
| 173 | + valueAfter: 0, // Value for slides after current |
| 174 | + offset: 0.2, // Animation offset (optional) |
| 175 | + }) |
| 176 | + |
| 177 | + return { |
| 178 | + opacity: progress, |
| 179 | + transform: [{ scale: progress }], |
| 180 | + } |
| 181 | +}) |
| 182 | +``` |
| 183 | + |
| 184 | +## Examples |
| 185 | + |
| 186 | +We provide a comprehensive example app showcasing all the carousel features. You can run the examples locally or view the source code: |
| 187 | + |
| 188 | +### 🏃♂️ Running the Example App |
| 189 | + |
| 190 | +```bash |
| 191 | +cd example |
| 192 | +pnpm install |
| 193 | +pnpm start |
| 194 | +``` |
| 195 | + |
| 196 | +Then scan the QR code with Expo Go or run on simulator. See the [example app README](./example/README.md) for detailed setup instructions. |
| 197 | + |
| 198 | +### 📱 Available Examples |
| 199 | + |
| 200 | +| Example | Description | Source Code | |
| 201 | +| ---------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------- | |
| 202 | +| **Basic Carousel** | Simple auto-scrolling image carousel | [`BasicExample.tsx`](./example/examples/BasicExample.tsx) | |
| 203 | +| **Animated Carousel** | Custom animations with scale, rotation, and opacity | [`AnimatedExample.tsx`](./example/examples/AnimatedExample.tsx) | |
| 204 | +| **Video Carousel** | Video playback with play/pause controls | [`VideoCarouselExample.tsx`](./example/examples/VideoCarouselExample.tsx) | |
| 205 | +| **Timer Pagination** | Visual progress indicators with custom intervals | [`TimerPaginationExample.tsx`](./example/examples/TimerPaginationExample.tsx) | |
| 206 | +| **Entering Animation** | Advanced slide entrance animations | [`EnteringAnimationExample.tsx`](./example/examples/EnteringAnimationExample.tsx) | |
| 207 | +| **Offset Example** | Custom slide positioning and spacing | [`OffsetExample.tsx`](./example/examples/OffsetExample.tsx) | |
| 208 | + |
| 209 | +### 🎯 Key Example Features |
| 210 | + |
| 211 | +- **Image Carousels** with smooth transitions and auto-scrolling |
| 212 | +- **Video Integration** with `expo-video` and playback controls |
| 213 | +- **Custom Animations** using `interpolateInsideCarousel` utility |
| 214 | +- **Timer-based Pagination** with visual progress bars |
| 215 | +- **Gesture Handling** with swipe navigation and user interaction detection |
| 216 | +- **Performance Optimization** with image preloading and memoization |
| 217 | + |
| 218 | +### 📍 Pagination Examples |
| 219 | + |
| 220 | +The library includes several pagination components and examples: |
| 221 | + |
| 222 | +- **Basic Pagination** - Simple dot indicators showing current slide ([`Pagination.tsx`](./example/examples/components/Pagination.tsx)) |
| 223 | +- **Timer Pagination** - Animated progress bars with customizable intervals ([`TimerPagination.tsx`](./example/examples/components/TimerPagination.tsx)) |
| 224 | +- **Custom Pagination** - Build your own pagination using `useCarouselContext()` hook for access to `scrollValue` and `timeoutValue` |
| 225 | + |
| 226 | +All pagination components automatically sync with the carousel state and support: |
| 227 | + |
| 228 | +- ✅ **Real-time updates** as slides change |
| 229 | +- ✅ **Timer progress visualization** with animated fill |
| 230 | +- ✅ **User interaction detection** (pause on touch) |
| 231 | +- ✅ **Custom styling** and animations |
| 232 | + |
| 233 | +## Advanced Usage |
| 234 | + |
| 235 | +### Programmatic Navigation |
| 236 | + |
| 237 | +Control the carousel programmatically using the context: |
| 238 | + |
| 239 | +```tsx |
| 240 | +const CarouselWithControls = () => { |
| 241 | + const { scrollValue } = useCarouselContext() |
| 242 | + const { runAutoScroll } = useAutoCarouselSlideIndex() |
| 243 | + |
| 244 | + const goToNext = () => { |
| 245 | + runAutoScroll(0) // Immediate transition |
| 246 | + } |
| 247 | + |
| 248 | + const goToSlide = (slideIndex: number) => { |
| 249 | + scrollValue.value = withTiming(slideIndex, { duration: 500 }) |
| 250 | + } |
| 251 | + |
| 252 | + return ( |
| 253 | + <View> |
| 254 | + <HeroCarousel disableAutoScroll>{/* Your slides */}</HeroCarousel> |
| 255 | + |
| 256 | + <View style={styles.controls}> |
| 257 | + <Button title="Previous" onPress={() => goToSlide(scrollValue.value - 1)} /> |
| 258 | + <Button title="Next" onPress={goToNext} /> |
| 259 | + </View> |
| 260 | + </View> |
| 261 | + ) |
| 262 | +} |
| 263 | +``` |
| 264 | + |
| 265 | +## Performance Tips |
| 266 | + |
| 267 | +1. **Image Optimization**: Use optimized image formats and appropriate resolutions |
| 268 | +2. **Preloading**: Preload images/videos for smoother transitions |
| 269 | +3. **Memoization**: Wrap slide components in `React.memo()` when possible |
| 270 | +4. **Worklet Functions**: Keep animations in worklet functions for 60fps performance |
| 271 | + |
| 272 | +```tsx |
| 273 | +// Good: Memoized slide component |
| 274 | +const SlideComponent = React.memo(({ data }) => { |
| 275 | + // Your slide content |
| 276 | +}) |
| 277 | + |
| 278 | +// Good: Preload images |
| 279 | +useEffect(() => { |
| 280 | + images.forEach((uri) => Image.prefetch(uri)) |
| 281 | +}, []) |
| 282 | +``` |
| 283 | + |
| 284 | +## Troubleshooting |
| 285 | + |
| 286 | +### Common Issues |
| 287 | + |
| 288 | +**Carousel not auto-scrolling:** |
| 289 | + |
| 290 | +- Ensure `CarouselContextProvider` wraps your carousel |
| 291 | +- Check if `disableAutoScroll` is set to `false` |
| 292 | +- Verify React Native Reanimated is properly installed |
| 293 | + |
| 294 | +**Animations not smooth:** |
| 295 | + |
| 296 | +- Make sure animations are running in worklet functions |
| 297 | +- Use `runOnUI` for heavy computations |
| 298 | +- Avoid heavy operations in animation callbacks |
| 299 | + |
| 300 | +**Infinite scroll not working:** |
| 301 | + |
| 302 | +- Ensure you have at least 2 slides |
| 303 | +- Check if slide widths are properly configured |
| 304 | + |
| 305 | +## Contributing |
| 306 | + |
| 307 | +Contributions are welcome! Please read our contributing guidelines and submit pull requests to our GitHub repository. |
| 308 | + |
| 309 | +## License |
| 310 | + |
| 311 | +MIT License - see the [LICENSE](LICENSE) file for details. |
| 312 | + |
| 313 | +## Credits |
| 314 | + |
| 315 | +Built with ❤️ using: |
| 316 | + |
| 317 | +- [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/) for animations |
| 318 | +- [React Native Gesture Handler](https://docs.swmansion.com/react-native-gesture-handler/) for gestures |
| 319 | + |
| 320 | +--- |
| 321 | + |
| 322 | +Made by [STRV](https://www.strv.com) 🚀 |
0 commit comments