Skip to content

Image Carousel

Image Carousel provides a feature-rich solution for displaying multiple images with smooth animations, automatic playback, and interactive controls. Built with React Native Reanimated for optimal performance and includes fullscreen modal support, customizable pagination, and flexible styling options.

import { ImageCarousel } from 'rnc-theme';
const images = [
{ image: require('./assets/image1.jpg') },
{ image: require('./assets/image2.jpg') },
{ image: require('./assets/image3.jpg') },
];
<ImageCarousel data={images} />
PropTypeDefaultDescription
dataCarouselItem[]-Array of carousel items to display
autoPlaybooleanfalseEnable automatic slide progression
autoPlayIntervalnumber3000Time between auto slides (ms)
paginationbooleantrueShow pagination dots
paginationPosition'bottom' | 'overlay''bottom'Position of pagination dots
paginationStyleStyleProp<ViewStyle>-Custom pagination container style
dotStyleStyleProp<ViewStyle>-Style for inactive dots
activeDotStyleStyleProp<ViewStyle>-Style for active dot
fullscreenbooleanfalseEnable fullscreen modal on tap
imageStyleStyleProp<ImageStyle>-Custom image styling
containerStyleStyleProp<ViewStyle>-Custom container styling
loopbooleantrueEnable infinite loop
widthDimensionValuewindowWidth * 0.7Carousel width
heightDimensionValue-Carousel height
showControlsControlsConfigSee belowControl visibility settings
controlsStyleControlsStyleSee belowControl styling options
PropertyTypeDefaultDescription
arrowsbooleanfalseShow navigation arrows
paginationbooleantrueShow pagination dots
counterbooleanfalseShow current slide counter
PropertyTypeDefaultDescription
arrowColorstring'white'Arrow icon color
arrowSizenumber24Arrow icon size
arrowBackgroundstring'rgba(0, 0, 0, 0.5)'Arrow background color
arrowBorderRadiusnumber20Arrow button border radius
counterBackgroundstring'rgba(0, 0, 0, 0.5)'Counter background color
counterTextColorstring'white'Counter text color
topViewStyle['top']'45%'Vertical position of arrows
PropertyTypeDescription
keystringOptional unique identifier
imageImageSourcePropTypeImage source (local or remote)
titlestringOptional image title
descriptionstringOptional image description
const PhotoGallery = () => {
const photos = [
{
image: { uri: 'https://example.com/photo1.jpg' },
title: 'Sunset Beach',
description: 'Beautiful sunset at the beach'
},
{
image: { uri: 'https://example.com/photo2.jpg' },
title: 'Mountain View',
description: 'Panoramic mountain landscape'
},
{
image: { uri: 'https://example.com/photo3.jpg' },
title: 'City Lights',
description: 'Night cityscape with lights'
},
];
return (
<ImageCarousel
data={photos}
fullscreen={true}
autoPlay={true}
autoPlayInterval={5000}
showControls={{
arrows: true,
pagination: true,
counter: true,
}}
containerStyle={{
marginVertical: 20,
backgroundColor: '#000',
borderRadius: 16,
overflow: 'hidden',
}}
/>
);
};
const ProductShowcase = ({ product }) => {
return (
<View style={{ padding: 16 }}>
<ImageCarousel
data={product.images}
width="100%"
height={300}
fullscreen={true}
paginationPosition="overlay"
showControls={{
arrows: false,
pagination: true,
counter: true,
}}
dotStyle={{
backgroundColor: 'rgba(255, 255, 255, 0.6)',
}}
activeDotStyle={{
backgroundColor: '#ffffff',
}}
controlsStyle={{
counterBackground: 'rgba(0, 0, 0, 0.8)',
counterTextColor: '#ffffff',
}}
imageStyle={{
borderRadius: 12,
}}
/>
</View>
);
};
const HeroBanner = () => {
const banners = [
{
image: require('./assets/banner1.jpg'),
title: 'Summer Sale',
description: 'Up to 50% off on all items'
},
{
image: require('./assets/banner2.jpg'),
title: 'New Collection',
description: 'Discover our latest arrivals'
},
{
image: require('./assets/banner3.jpg'),
title: 'Free Shipping',
description: 'On orders over $50'
},
];
return (
<ImageCarousel
data={banners}
autoPlay={true}
autoPlayInterval={4000}
loop={true}
width="100%"
height={200}
showControls={{
arrows: true,
pagination: true,
counter: false,
}}
controlsStyle={{
arrowColor: '#ffffff',
arrowBackground: 'rgba(0, 0, 0, 0.6)',
arrowBorderRadius: 25,
arrowSize: 28,
}}
paginationPosition="overlay"
dotStyle={{
backgroundColor: 'rgba(255, 255, 255, 0.5)',
width: 12,
height: 4,
}}
activeDotStyle={{
backgroundColor: '#ffffff',
width: 24,
}}
/>
);
};
const ThumbnailGallery = ({ images }) => {
const [selectedIndex, setSelectedIndex] = useState(0);
return (
<View>
{/* Main Carousel */}
<ImageCarousel
data={images}
fullscreen={true}
pagination={false}
showControls={{
arrows: true,
pagination: false,
counter: true,
}}
width="100%"
height={400}
onIndexChange={setSelectedIndex}
/>
{/* Thumbnail Strip */}
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
style={{ marginTop: 16 }}
>
{images.map((item, index) => (
<TouchableOpacity
key={index}
onPress={() => setSelectedIndex(index)}
style={[
styles.thumbnail,
selectedIndex === index && styles.activeThumbnail
]}
>
<Image
source={item.image}
style={styles.thumbnailImage}
resizeMode="cover"
/>
</TouchableOpacity>
))}
</ScrollView>
</View>
);
};
const styles = StyleSheet.create({
thumbnail: {
width: 60,
height: 60,
marginRight: 8,
borderRadius: 8,
overflow: 'hidden',
borderWidth: 2,
borderColor: 'transparent',
},
activeThumbnail: {
borderColor: '#007AFF',
},
thumbnailImage: {
width: '100%',
height: '100%',
},
});
const CustomPaginationCarousel = () => {
return (
<ImageCarousel
data={images}
pagination={true}
paginationPosition="bottom"
paginationStyle={{
backgroundColor: 'rgba(255, 255, 255, 0.9)',
borderRadius: 20,
paddingHorizontal: 16,
marginHorizontal: 20,
marginBottom: 10,
}}
dotStyle={{
backgroundColor: '#cccccc',
width: 8,
height: 8,
borderRadius: 4,
marginHorizontal: 2,
}}
activeDotStyle={{
backgroundColor: '#007AFF',
width: 20,
height: 8,
borderRadius: 4,
}}
/>
);
};
const ControlledCarousel = () => {
const carouselRef = useRef<ImageCarouselRef>(null);
const [currentIndex, setCurrentIndex] = useState(0);
const goToSlide = (index: number) => {
carouselRef.current?.scrollToIndex(index);
};
const nextSlide = () => {
carouselRef.current?.scrollToNext();
};
const prevSlide = () => {
carouselRef.current?.scrollToPrev();
};
return (
<View>
<ImageCarousel
ref={carouselRef}
data={images}
onIndexChange={setCurrentIndex}
showControls={{
arrows: false,
pagination: false,
counter: false,
}}
/>
<View style={styles.customControls}>
<TouchableOpacity onPress={prevSlide}>
<Text>Previous</Text>
</TouchableOpacity>
<Text>{currentIndex + 1} of {images.length}</Text>
<TouchableOpacity onPress={nextSlide}>
<Text>Next</Text>
</TouchableOpacity>
</View>
<View style={styles.thumbnailControls}>
{images.map((_, index) => (
<TouchableOpacity
key={index}
onPress={() => goToSlide(index)}
style={[
styles.thumbnailButton,
currentIndex === index && styles.activeThumbnailButton
]}
>
<Text>{index + 1}</Text>
</TouchableOpacity>
))}
</View>
</View>
);
};
const DynamicImageCarousel = () => {
const [images, setImages] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadImages();
}, []);
const loadImages = async () => {
try {
const response = await fetch('/api/images');
const imageData = await response.json();
setImages(imageData);
} catch (error) {
console.error('Failed to load images:', error);
} finally {
setLoading(false);
}
};
if (loading) {
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" />
<Text>Loading images...</Text>
</View>
);
}
return (
<ImageCarousel
data={images}
autoPlay={true}
fullscreen={true}
showControls={{
arrows: true,
pagination: true,
counter: true,
}}
onImageError={(error, index) => {
console.error(`Failed to load image at index ${index}:`, error);
}}
/>
);
};

The carousel uses React Native Reanimated for smooth, performant animations:

  • Width: Interpolates between 8px (inactive) and 16px (active)
  • Opacity: Transitions from 0.5 to 1.0 for active state
  • Scale: Active dot scales to 1.2x with spring animation
  • Scale: Images scale from 0.8 to 1.0 based on scroll position
  • Smooth Transitions: Uses interpolation for fluid movement
  • Fade Animation: Modal appears with fade transition
  • Status Bar: Automatically hidden in fullscreen mode
  • Close Button: Custom X icon with smooth animations

Optimization Tips

  • Use React.memo for carousel items when data changes frequently
  • Implement lazy loading for large image sets
  • Consider image caching for remote images
  • Use appropriate image sizes to reduce memory usage

Memory Management

  • Implement image recycling for very large datasets
  • Use removeClippedSubviews for long lists
  • Monitor memory usage with large images
  • Consider using placeholder images during loading

User Experience

  • Always provide loading states for remote images
  • Use appropriate aspect ratios for consistent layout
  • Consider accessibility labels for screen readers
  • Provide clear visual feedback for interactive elements

Design Guidelines

  • Maintain consistent spacing and styling
  • Use appropriate contrast for controls
  • Consider touch target sizes (minimum 44px)
  • Provide visual hierarchy with proper sizing

Images not displaying:

  • Ensure image sources are correctly formatted
  • Check network connectivity for remote images
  • Verify local image paths and imports

Performance issues:

  • Reduce image sizes and implement lazy loading
  • Use getItemLayout for better scroll performance
  • Consider virtualization for large datasets

Animation stuttering:

  • Ensure React Native Reanimated is properly installed
  • Check for blocking operations on the UI thread
  • Use useNativeDriver where possible