• Sign in
  • Get all-access
  • Docs
  • Components
  • Templates
carousel
  • carousel

carousel

Android
Apple
import React, { useRef } from "react";
import {
  Animated,
  FlatList,
  FlatListProps,
  NativeScrollEvent,
  NativeSyntheticEvent,
  useWindowDimensions,
} from "react-native";
import { Box } from "@/components/ui/box";

type CarouselFlatListProps<T> = FlatListProps<T> & {
  innerRef?: React.RefObject<any> | null;
  className?: string;
  withPagination?: boolean;
  dotActiveColor?: string;
  dotColor?: string;
  dotClassName?: string;
};

function CarouselPagination<ItemT>({
  data,
  scrollX,
  dotActiveColor = "#5852a5",
  dotColor = "#ccc",
  className,
}: {
  data: ArrayLike<ItemT> | null | undefined;
  scrollX: Animated.Value;
  dotActiveColor?: string;
  dotColor?: string;
  className?: string;
}) {
  const { width } = useWindowDimensions();

  if (!data || !Array.isArray(data)) {
    return null;
  }

  return (
    <Box
      className={`flex flex-row ${className || "mt-6 w-full justify-center items-center"}`}
    >
      {data.map((_, idx) => {
        const inputRange = [(idx - 1) * width, idx * width, (idx + 1) * width];

        const dotWidth = scrollX.interpolate({
          inputRange,
          outputRange: [12, 30, 12],
          extrapolate: "clamp",
        });

        const backgroundColor = scrollX.interpolate({
          inputRange,
          outputRange: [dotColor, dotActiveColor, dotColor],
          extrapolate: "clamp",
        });

        return (
          <Animated.View
            key={idx.toString()}
            className={"w-2 h-2"}
            style={[
              {
                borderRadius: 6,
                marginHorizontal: 3,
                backgroundColor: dotColor,
              },
              { width: dotWidth, backgroundColor },
            ]}
          />
        );
      })}
    </Box>
  );
}

export default function Carousel<ItemT>({
  dotActiveColor = "#fff",
  dotColor = "#ddd",
  dotClassName = "",
  withPagination = true,
  innerRef,
  ...props
}: CarouselFlatListProps<ItemT>) {
  const scrollX = useRef(new Animated.Value(0)).current;

  const handleOnScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
    Animated.event(
      [
        {
          nativeEvent: {
            contentOffset: {
              x: scrollX,
            },
          },
        },
      ],
      {
        useNativeDriver: false,
      },
    )(event);
  };

  const viewabilityConfig = useRef({
    itemVisiblePercentThreshold: 50,
  }).current;

  return (
    <>
      <FlatList
        ref={innerRef}
        {...props}
        horizontal={true}
        decelerationRate={"fast"}
        pagingEnabled={withPagination}
        snapToAlignment="center"
        showsHorizontalScrollIndicator={false}
        onScroll={(e) => {
          props.onScroll?.(e);
          handleOnScroll(e);
        }}
        viewabilityConfig={viewabilityConfig}
      />
      {withPagination && (
        <CarouselPagination
          data={props.data}
          scrollX={scrollX}
          dotActiveColor={dotActiveColor}
          dotColor={dotColor}
          className={dotClassName}
        />
      )}
    </>
  );
}
import { Box } from "@/components/ui/box";
import { Text } from "@/components/ui/text";
import { SafeAreaView } from "react-native-safe-area-context";
import { StatusBar } from "expo-status-bar";
import { useWindowDimensions } from "react-native";
import Carousel from "@/nativestack-ui/components/carousel/free/carousel";

export default function TabHomeScreen() {
  const items = [1, 2, 3];
  const { width, height } = useWindowDimensions();

  return (
    <SafeAreaView className={"bg-white w-full h-full"}>
      <StatusBar style={"light"} backgroundColor={"#fff"}></StatusBar>
      <Box>
        <Carousel
          dotActiveColor={"#434343"}
          data={items}
          renderItem={({ item }) => {
            return (
              <Box>
                <Box
                  style={{
                    width,
                    height: height / 2,
                  }}
                  className={`${item % 2 === 0 ? "bg-indigo-500" : "bg-indigo-300"} flex justify-center items-center`}
                >
                  <Text className={"text-typography-900"} size={"3xl"}>
                    {item}
                  </Text>
                </Box>
              </Box>
            );
          }}
        />
      </Box>
    </SafeAreaView>
  );
}

© 2025 NativeStack UI. All rights reserved.

Privacy policy
Changelog