Usage

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 0 is progress 0;
  • 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.