Skip to main content

Basic Snap Points

Create a bottom sheet that snaps to multiple heights:
import { interpolate } from "react-native-reanimated";

options={{
  snapPoints: [0.5, 1],           // Snap at 50% and 100% of screen height
  initialSnapIndex: 0,             // Start at 50%
  screenStyleInterpolator: ({ progress }) => {
    "worklet";
    return {
      contentStyle: {
        opacity: interpolate(progress, [0, 1, 2], [0, 1, 0]),
      },
    };
  },
}}
Snap points are numeric fractions from 0 to 1:
  • 0 = fully collapsed (bottom edge)
  • 0.5 = halfway up screen
  • 1 = fully expanded (top edge)

How Snap Points Work

When snapPoints is set:
  • Users can drag and release
  • The gesture snaps to the nearest snap point
  • The snapIndex is available in interpolators
  • The animation eases to the target snap point
screenStyleInterpolator: ({ progress, snapIndex }) => {
  "worklet";
  // snapIndex tells you which point was snapped to
  return {
    contentStyle: {
      // Respond to snap index
    },
  };
};

Gesture Control with Snap Points

Enable gestures to allow users to swipe between snap points:
options={{
  snapPoints: [0.5, 1],
  initialSnapIndex: 0,
  gestureEnabled: true,
  gestureDirection: "vertical",
  gestureActivationArea: "screen",
}}
Now users can:
  1. Swipe down from 100% to collapse to 50%
  2. Swipe up from 50% to expand to 100%
  3. Swipe down from 50% to dismiss completely

Snap Points Options

OptionTypeDefaultDescription
snapPointsnumber[]Fractions 0-1 for each stop
initialSnapIndexnumber0Which snap point to start at
gestureSnapLockedbooleanfalseLock to snap points only
backdropBehaviorstring”dismiss”How backdrop responds
backdropComponentcomponentdefaultCustom backdrop
expandViaScrollViewbooleantrueSwipe up expands from boundary

Backdrop Behavior

Control what happens when users interact with the backdrop:
options={{
  backdropBehavior: "dismiss",     // Swipe dismisses sheet
  backdropBehavior: "collapse",    // Swipe collapses to previous point
  backdropBehavior: "block",       // No gesture response
  backdropBehavior: "passthrough", // Tap passes to underlying screen
}}

expandViaScrollView

When expandViaScrollView is true (default), the sheet expands when the user swipes up from the boundary:
import Transition from "react-native-screen-transitions";

function DetailSheet() {
  return (
    <Transition.ScrollView style={{ flex: 1 }}>
      <Text>Swipe up to expand to full screen</Text>
    </Transition.ScrollView>
  );
}

options={{
  snapPoints: [0.5, 1],
  expandViaScrollView: true,  // Default
}}
If false, the sheet expands only via deadspace above the scroll content.

Custom Backdrop

Replace the default dark overlay:
import Animated, { interpolate } from "react-native-reanimated";

function CustomBackdrop({ progress }) {
  "worklet";
  return (
    <Animated.View
      style={{
        flex: 1,
        backgroundColor: "#000",
        opacity: interpolate(progress, [0, 1, 2], [0, 0.3, 0]),
      }}
    />
  );
}

options={{
  snapPoints: [0.5, 1],
  backdropComponent: CustomBackdrop,
}}

Programmatic Snap Control

Use the snapTo function to snap programmatically:
import { snapTo } from "react-native-screen-transitions";

function DetailSheet({ navigation }) {
  return (
    <View style={{ flex: 1, paddingBottom: 20 }}>
      <TouchableOpacity
        onPress={() => {
          snapTo(1);  // Snap to index 1 (fully expanded)
        }}
      >
        <Text>Expand</Text>
      </TouchableOpacity>
    </View>
  );
}

Bottom Sheet Example

A full bottom sheet with multiple snap points:
import { createBlankStackNavigator } from "react-native-screen-transitions/blank-stack";
import Transition from "react-native-screen-transitions";
import { View, Text, ScrollView, TouchableOpacity } from "react-native";
import Animated, { interpolate } from "react-native-reanimated";

const Stack = createBlankStackNavigator();

function HomeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, justifyContent: "center" }}>
      <TouchableOpacity onPress={() => navigation.navigate("Sheet")}>
        <Text>Open Sheet</Text>
      </TouchableOpacity>
    </View>
  );
}

function SheetScreen() {
  return (
    <Transition.ScrollView
      style={{ flex: 1, backgroundColor: "#fff" }}
      scrollEnabled={true}
    >
      <View style={{ padding: 20 }}>
        <Text style={{ fontSize: 20, fontWeight: "bold", marginBottom: 10 }}>
          Options
        </Text>
        <Text>Swipe to adjust the sheet height</Text>
      </View>
    </Transition.ScrollView>
  );
}

export default function App() {
  return (
    <Stack.Navigator screenOptions={{ headerShown: false }}>
      <Stack.Screen name="Home" component={HomeScreen} />
      <Stack.Screen
        name="Sheet"
        component={SheetScreen}
        options={{
          snapPoints: [0.5, 0.75, 1],
          initialSnapIndex: 0,
          screenStyleInterpolator: ({ progress }) => {
            "worklet";
            return {
              contentStyle: {
                borderTopLeftRadius: 16,
                borderTopRightRadius: 16,
                transform: [
                  {
                    translateY: interpolate(progress, [0, 1, 2], [600, 0, 600]),
                  },
                ],
              },
              backdropStyle: {
                opacity: interpolate(progress, [0, 1, 2], [0, 0.3, 0]),
              },
            };
          },
          transitionSpec: {
            open: {
              timing: "spring",
              config: { stiffness: 800, damping: 100 },
            },
            close: {
              timing: "spring",
              config: { stiffness: 800, damping: 100 },
            },
            expand: {
              timing: "spring",
              config: { stiffness: 600, damping: 80 },
            },
            collapse: {
              timing: "spring",
              config: { stiffness: 600, damping: 80 },
            },
          },
          gestureEnabled: true,
          gestureDirection: "vertical",
          backdropBehavior: "collapse",
        }}
      />
    </Stack.Navigator>
  );
}

Side Sheet Example

Create a side sheet using horizontal snap points:
<Stack.Screen
  name="SideSheet"
  component={SideSheetScreen}
  options={{
    snapPoints: [0, 0.6, 1],
    initialSnapIndex: 1,
    screenStyleInterpolator: ({ progress }) => {
      "worklet";
      return {
        contentStyle: {
          transform: [
            {
              translateX: interpolate(progress, [0, 1, 2], [400, 0, 400]),
            },
          ],
        },
        backdropStyle: {
          opacity: interpolate(progress, [0, 1, 2], [0, 0.3, 0]),
        },
      };
    },
    gestureEnabled: true,
    gestureDirection: "horizontal",
    backdropBehavior: "dismiss",
  }}
/>

Animation Specs for Snap Points

Use transitionSpec to configure expand and collapse animations:
options={{
  snapPoints: [0.5, 1],
  transitionSpec: {
    expand: {
      timing: "spring",
      config: { stiffness: 600, damping: 60, mass: 1 },
    },
    collapse: {
      timing: "spring",
      config: { stiffness: 600, damping: 60, mass: 1 },
    },
  },
}}

Next Steps