Usage
Wrap your application in Gesture Handler's root view, then render the named Carousel export:
import * as React from "react";
import { Text, useWindowDimensions, View } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { Carousel } from "react-native-reanimated-carousel";
const data = ["One", "Two", "Three"];
export default function App() {
const { width } = useWindowDimensions();
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<View style={{ flex: 1, justifyContent: "center" }}>
<Carousel
data={data}
style={{ width, height: 220 }}
renderItem={({ item, index }) => (
<View
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
backgroundColor: index % 2 ? "#DCE7F7" : "#F5E7D3",
}}
>
<Text>{item}</Text>
</View>
)}
/>
</View>
</GestureHandlerRootView>
);
}loop defaults to false. Add loop explicitly for infinite wrapping.
Sizing
style controls the viewport. If itemSize is omitted, the page distance is the measured main-axis size.
Fixed size
<Carousel
style={{ width: 320, height: 180 }}
data={data}
renderItem={renderItem}
/>Responsive size
The parent must provide enough constraints for the viewport to measure:
<View style={{ height: 220 }}>
<Carousel
style={{ flex: 1 }}
data={data}
renderItem={renderItem}
/>
</View>Multiple visible items
Set itemSize when the snap distance should be smaller than the viewport:
<Carousel
style={{ width: 360, height: 180 }}
itemSize={240}
data={data}
renderItem={renderItem}
/>For a vertical carousel, use the same prop names:
<Carousel
orientation="vertical"
style={{ width: 240, height: 480 }}
itemSize={180}
data={data}
renderItem={renderItem}
/>Pagination
Create one logical progress SharedValue and give it to both components:
import * as React from "react";
import { View } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import {
Carousel,
Pagination,
type CarouselRef,
} from "react-native-reanimated-carousel";
function CarouselWithPagination() {
const ref = React.useRef<CarouselRef>(null);
const progress = useSharedValue(0);
const data = ["One", "Two", "Three"];
return (
<View>
<Carousel
ref={ref}
data={data}
progress={progress}
style={{ height: 220 }}
renderItem={renderItem}
/>
<Pagination
count={data.length}
progress={progress}
containerStyle={{ gap: 8, justifyContent: "center", marginTop: 12 }}
dotStyle={{ width: 8, height: 8, backgroundColor: "#CBD5E1" }}
activeDotStyle={{ width: 20, backgroundColor: "#0F172A" }}
onPress={(index) => ref.current?.scrollTo({ index })}
getItemAccessibilityLabel={(index, count) =>
`Featured item ${index + 1} of ${count}`
}
/>
</View>
);
}With onPress, Pagination dots are accessible buttons. Without onPress, they are decorative and removed from the accessibility tree.
Progress
progress is the UI-thread option. It is a fractional logical index:
- item
0is progress0; - moving forward increases progress;
- non-loop progress is bounded;
- loop progress is continuous and can cross multiple cycles.
Use onProgressChange only when JS needs each frame:
<Carousel
data={data}
renderItem={renderItem}
onProgressChange={(progress) => {
// Runs on JS. Keep this work small.
}}
/>Pixel translation is a separate advanced surface:
const offset = useSharedValue(0);
<Carousel
data={data}
renderItem={renderItem}
scrollOffsetValue={offset}
/>Forward one page is -itemSize pixels. External writes move the content but do not commit selection; call scrollTo({ index }) to settle on an item.
Layouts
Built-in layouts use one flat discriminated object:
<Carousel
data={data}
renderItem={renderItem}
layout={{
type: "parallax",
offset: 100,
scale: 0.8,
}}
/>Stack:
<Carousel
data={data}
renderItem={renderItem}
layout={{
type: "horizontal-stack",
visibleCount: 4,
spacing: 18,
exitDirection: "left",
}}
/>For custom effects, use itemAnimation instead of layout; TypeScript prevents passing both.
Autoplay
<Carousel
data={data}
renderItem={renderItem}
loop
autoplay
autoplayInterval={3000}
autoplayDirection="forward"
/>autoplayInterval is settled dwell time: animation duration is not part of the interval. When loop={false}, autoplay stops at the boundary.
scrollEnabled={false} disables user gestures only. Autoplay and ref commands remain available.
Accessibility
Only the current slide is exposed to accessibility services. Provide meaningful slide content inside renderItem and compose external headings, navigation buttons, or an interactive Pagination where appropriate.