About this animation
A tactile illustration carousel whose full-screen blurred backdrop follows every fraction of the drag.
How it works
- The component measures its own container and derives card geometry from its width, so it fits phones, tablets, and nested layouts.
- One continuous
positionshared value drives the cards, backdrop opacity, title depth, veils, and stretching pagination dots on the UI thread. - Every backdrop stays mounted. Its opacity is the inverse distance from the continuous position, producing a direct crossfade between neighboring illustrations while the finger moves.
- A projected release velocity selects the destination, then an interruptible spring settles the carousel. Taps, screen-reader actions, and the imperative ref use the same path.
blurSourcecan supply a smaller version of each image;expo-imageapplies the 50-point blur to that inexpensive source while the card retains full resolution.
Why this one
Unlike the common index-based pattern, the backdrop never waits for a page-change event. It blends continuously through the drag. The centered card also gains physical priority through scale, lift, layering, a two-part shadow, and a short traveling highlight, while neighboring cards sit lower with a veil and turn toward the center. Pagination pills stretch fluidly instead of jumping between fixed dots.
Install
npx expo install expo-image expo-linear-gradient react-native-gesture-handler react-native-reanimated react-native-workletsUsage
import BlurredBackdropCarousel from './BlurredBackdropCarousel';
export function Gallery() {
return (
<BlurredBackdropCarousel
items={[
{
image: require('./assets/orbit.jpg'),
blurSource: require('./assets/orbit-small.jpg'),
title: 'Beyond Orbit',
subtitle: 'Dreams without gravity',
},
{
image: require('./assets/wildlight.jpg'),
title: 'Wildlight',
subtitle: 'Stories from the forest edge',
},
]}
/>
);
}Use the personality knobs and ref for a wider, externally controlled presentation:
import { useRef } from 'react';
import BlurredBackdropCarousel, {
type BlurredBackdropCarouselRef,
} from './BlurredBackdropCarousel';
export function FeatureGallery() {
const carouselRef = useRef<BlurredBackdropCarouselRef>(null);
return (
<BlurredBackdropCarousel
ref={carouselRef}
cardWidth={0.7}
cardAspect={1.5}
fontFamily="YourSansFont"
items={collections}
titleFontFamily="YourDisplayFont"
onIndexChange={(index) => trackCollection(index)}
/>
);
}The forwarded ref exposes scrollTo(index).
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | { image; blurSource?; title?; subtitle? }[] | Required | Artwork, optional low-resolution blur source, and copy for each card. |
cardWidth | number | 0.62 | Card width as a fraction of the component width; clamped from 0.4 to 0.82. |
cardAspect | number | 1.45 | Card height divided by card width; clamped from 0.8 to 1.8. |
fontFamily | string | System font | Font for subtitles and empty-state copy. |
titleFontFamily | string | System font | Font for collection titles. |
onIndexChange | (index: number) => void | — | Called when the nearest centered item changes. |
style | StyleProp<ViewStyle> | — | Style for the outer container. |
testID | string | — | Test identifier for the outer container. |
Each item accepts image, optional title and subtitle, and optional blurSource. Supplying a small blurSource reduces the cost of the full-screen blur.
Compatibility
- Expo SDK 57
- React Native 0.86
- Reanimated 4.5 and Worklets 0.10
- iOS: supported, not yet verified on a device
- Android: supported, not yet verified on a device
- Web: supported, verified by the project gate
- Works in Expo Go because all native dependencies are included in the SDK
Accessibility
The carousel exposes adjustable increment and decrement actions, and every card and pagination dot is a labeled button. Tapping a neighboring card is an alternative to dragging. With reduced motion enabled, drag parallax, perspective, springs, and the highlight sweep are removed; selection changes use a 200 ms crossfade.