Skip to main content

Enable Gestures

Make a screen dismissible by enabling the gesture:
options={{
  gestureEnabled: true,
  gestureDirection: "vertical",
}}
When enabled, users can drag to dismiss the screen. The animation progress follows the gesture in real time.

Gesture Options

All gesture options are individual props on screen options:
options={{
  gestureEnabled: true,
  gestureDirection: "vertical",
  gestureActivationArea: "edge",
  gestureResponseDistance: 20,
  gestureVelocityImpact: 0.3,
  gestureDrivesProgress: true,
  snapVelocityImpact: 0.1,
  expandViaScrollView: true,
  gestureSnapLocked: false,
  backdropBehavior: "dismiss",
  backdropComponent: MyBackdrop,
}}

Gesture Properties

PropertyTypeDefaultDescription
gestureEnabledbooleanfalseEnable swipe-to-dismiss
gestureDirectionstring | string[]“vertical”Direction: “horizontal”, “vertical”, “inverted”, or combinations
gestureActivationAreastring | object”edge”Activation zone: “edge”, “screen”, or per-side config
gestureResponseDistancenumber20Distance to activate (pixels)
gestureVelocityImpactnumber0.3Weight of velocity on final position (0-1)
gestureDrivesProgressbooleantrueGesture directly drives progress 0→1→2
snapVelocityImpactnumber0.1Weight of velocity on snap point selection
expandViaScrollViewbooleantrueSwipe up expands from boundary (snap points)
gestureSnapLockedbooleanfalseLock gesture to snap points
backdropBehaviorstring”dismiss”How backdrop responds to swipe
backdropComponentcomponentdefaultCustom backdrop renderer

Gesture Directions

Specify which direction(s) trigger the gesture:
// Single direction
gestureDirection: "vertical"
gestureDirection: "horizontal"
gestureDirection: "vertical-inverted"
gestureDirection: "horizontal-inverted"

// Bidirectional
gestureDirection: "bidirectional"

// Multiple directions (array)
gestureDirection: ["vertical", "horizontal"]
  • “vertical” — Swipe down to dismiss (top-to-bottom)
  • “vertical-inverted” — Swipe up to dismiss (bottom-to-top, useful for bottom sheets)
  • “horizontal” — Swipe left/right to dismiss
  • “horizontal-inverted” — Swipe right to dismiss (for RTL)
  • “bidirectional” — Any direction dismisses

Activation Areas

Control where on screen the gesture begins:
// Built-in presets
gestureActivationArea: "edge"    // Edges only
gestureActivationArea: "screen"  // Entire screen

// Custom per-side
gestureActivationArea: {
  top: 40,
  bottom: 40,
  left: 20,
  right: 20,
}
  • “edge” — Only the edges of the screen (useful to avoid conflicts with scrollable content)
  • “screen” — Anywhere on screen
  • Custom object — Specify pixels from each edge

Velocity & Snapping

Control how velocity affects the final animation:
options={{
  // How much the gesture velocity affects the final position
  gestureVelocityImpact: 0.3,  // Default

  // How much velocity affects snap point selection
  snapVelocityImpact: 0.1,     // Default
}}
Higher values make velocity more influential. Lower values ignore velocity.

ScrollView Coordination

When using Transition.ScrollView or Transition.FlatList, gestures automatically coordinate:
  • vertical — Gesture activates at top of scrollable content
  • vertical-inverted — Gesture activates at bottom
  • horizontal — Gesture activates at left/right edges
import Transition from "react-native-screen-transitions";

function DetailScreen() {
  return (
    <Transition.ScrollView
      style={{ flex: 1 }}
      bounces={false}
    >
      <Text>Content...</Text>
    </Transition.ScrollView>
  );
}

<Stack.Screen
  name="Detail"
  component={DetailScreen}
  options={{
    gestureEnabled: true,
    gestureDirection: "vertical",
  }}
/>
The scroll gesture takes priority. Only when the scroll reaches the top can the dismiss gesture activate.

Gesture-Driven Animations

The gesture directly drives the progress value (0→1→2):
  • Dragging down → progress increases from 1 → 2 (exiting)
  • Released → animation completes to 2 (dismissed) or reverts to 1 (stay)
  • The interpolator responds in real time to the gesture position
Access gesture state in your interpolator:
screenStyleInterpolator: ({ current }) => {
  "worklet";
  const { gesture } = current;

  return {
    contentStyle: {
      opacity: gesture.isDragging ? 0.5 : 1,
    },
  };
};

Gesture State

The gesture object in screen state includes:
PropertyTypeDescription
isDraggingbooleanUser is actively dragging
xAnimated ValueX position in pixels
yAnimated ValueY position in pixels
normalizedXAnimated ValueX as 0-1 fraction
normalizedYAnimated ValueY as 0-1 fraction
isDismissingbooleanGesture will dismiss
directionstring”up”, “down”, “left”, “right”

Backdrop Behavior

Control how the backdrop (overlay) responds to swiping:
options={{
  backdropBehavior: "dismiss",     // Swipe dismisses
  backdropBehavior: "collapse",    // Swipe collapses (snap points)
  backdropBehavior: "block",       // Swipe disabled
  backdropBehavior: "passthrough", // Passthrough to underlying screen
}}
  • “dismiss” — Swiping anywhere dismisses
  • “collapse” — Swiping snaps to previous point
  • “block” — Backdrop blocks all interaction
  • “passthrough” — Taps pass to screen below

Custom Backdrop Component

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

options={{
  backdropComponent: MyBackdrop,
  backdropBehavior: "dismiss",
}}

Full Gesture Example

function HomeScreen({ navigation }) {
  return (
    <TouchableOpacity onPress={() => navigation.navigate("Detail")}>
      <Text>Open modal</Text>
    </TouchableOpacity>
  );
}

function DetailScreen() {
  return (
    <View style={{ flex: 1, backgroundColor: "#fff" }}>
      <Text>Swipe down to dismiss</Text>
    </View>
  );
}

<Stack.Screen
  name="Detail"
  component={DetailScreen}
  options={{
    screenStyleInterpolator: ({ progress }) => {
      "worklet";
      return {
        contentStyle: {
          transform: [
            {
              translateY: interpolate(progress, [1, 2], [0, 200]),
            },
          ],
          opacity: interpolate(progress, [1, 2], [1, 0.7]),
        },
        backdropStyle: {
          opacity: interpolate(progress, [1, 2], [0.3, 0]),
        },
      };
    },
    gestureEnabled: true,
    gestureDirection: "vertical",
    gestureActivationArea: "screen",
    gestureVelocityImpact: 0.3,
    backdropBehavior: "dismiss",
  }}
/>

Next Steps