CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-native-gesture-handler

Declarative API exposing platform native touch and gesture system to React Native

Pending
Overview
Eval results
Files

advanced-components.mddocs/

Advanced Layout Components

Sophisticated layout and interaction components for complex UI patterns like swipe-to-reveal and drawer layouts.

Capabilities

Swipeable

Component for creating swipe-to-reveal interfaces, commonly used in messaging apps and todo lists for contextual actions.

/**
 * Swipeable component for creating swipe-to-reveal interfaces
 * Provides left and right swipe actions with customizable animations
 */
function Swipeable(props: {
  friction?: number;
  leftThreshold?: number;
  rightThreshold?: number;
  dragOffsetFromLeftEdge?: number;
  dragOffsetFromRightEdge?: number;
  overshootLeft?: boolean;
  overshootRight?: boolean;
  overshootFriction?: number;
  onSwipeableLeftOpen?: () => void;
  onSwipeableRightOpen?: () => void;
  onSwipeableOpen?: (direction: "left" | "right") => void;
  onSwipeableClose?: (direction: "left" | "right") => void;
  onSwipeableLeftWillOpen?: () => void;
  onSwipeableRightWillOpen?: () => void;
  onSwipeableWillOpen?: (direction: "left" | "right") => void;
  onSwipeableWillClose?: (direction: "left" | "right") => void;
  renderLeftActions?: (
    progressAnimatedValue: Animated.AnimatedAddition,
    dragX: Animated.AnimatedAddition
  ) => React.ReactNode;
  renderRightActions?: (
    progressAnimatedValue: Animated.AnimatedAddition,
    dragX: Animated.AnimatedAddition
  ) => React.ReactNode;
  useNativeAnimations?: boolean;
  animationOptions?: object;
  containerStyle?: StyleProp<ViewStyle>;
  childrenContainerStyle?: StyleProp<ViewStyle>;
  enableTrackpadTwoFingerGesture?: boolean;
  children: React.ReactNode;
}): JSX.Element;

interface SwipeableProps {
  friction?: number;
  leftThreshold?: number;
  rightThreshold?: number;
  dragOffsetFromLeftEdge?: number;
  dragOffsetFromRightEdge?: number;
  overshootLeft?: boolean;
  overshootRight?: boolean;
  overshootFriction?: number;
  onSwipeableLeftOpen?: () => void;
  onSwipeableRightOpen?: () => void;
  onSwipeableOpen?: (direction: "left" | "right") => void;
  onSwipeableClose?: (direction: "left" | "right") => void;
  onSwipeableLeftWillOpen?: () => void;
  onSwipeableRightWillOpen?: () => void;
  onSwipeableWillOpen?: (direction: "left" | "right") => void;
  onSwipeableWillClose?: (direction: "left" | "right") => void;
  renderLeftActions?: (
    progressAnimatedValue: Animated.AnimatedAddition,
    dragX: Animated.AnimatedAddition
  ) => React.ReactNode;
  renderRightActions?: (
    progressAnimatedValue: Animated.AnimatedAddition,
    dragX: Animated.AnimatedAddition
  ) => React.ReactNode;
  useNativeAnimations?: boolean;
  animationOptions?: object;
  containerStyle?: StyleProp<ViewStyle>;
  childrenContainerStyle?: StyleProp<ViewStyle>;
  enableTrackpadTwoFingerGesture?: boolean;
  children: React.ReactNode;
}

Usage Example:

import React, { useRef } from "react";
import { View, Text, Animated, TouchableOpacity } from "react-native";
import { Swipeable } from "react-native-gesture-handler";

function SwipeableItem({ item, onDelete, onArchive }) {
  const swipeableRef = useRef<Swipeable>(null);

  const renderLeftActions = (progress, dragX) => {
    const trans = dragX.interpolate({
      inputRange: [0, 50, 100, 101],
      outputRange: [-20, 0, 0, 1],
      extrapolate: "clamp",
    });

    return (
      <View style={{ flex: 1, backgroundColor: "green", justifyContent: "center" }}>
        <Animated.View style={{ transform: [{ translateX: trans }] }}>
          <TouchableOpacity
            onPress={() => {
              onArchive(item.id);
              swipeableRef.current?.close();
            }}
            style={{ paddingHorizontal: 20 }}
          >
            <Text style={{ color: "white", fontWeight: "bold" }}>Archive</Text>
          </TouchableOpacity>
        </Animated.View>
      </View>
    );
  };

  const renderRightActions = (progress, dragX) => {
    const trans = dragX.interpolate({
      inputRange: [-101, -100, -50, 0],
      outputRange: [1, 0, 0, 20],
      extrapolate: "clamp",
    });

    return (
      <View style={{ flex: 1, backgroundColor: "red", justifyContent: "center" }}>
        <Animated.View style={{ transform: [{ translateX: trans }] }}>
          <TouchableOpacity
            onPress={() => {
              onDelete(item.id);
              swipeableRef.current?.close();
            }}
            style={{ paddingHorizontal: 20 }}
          >
            <Text style={{ color: "white", fontWeight: "bold" }}>Delete</Text>
          </TouchableOpacity>
        </Animated.View>
      </View>
    );
  };

  return (
    <Swipeable
      ref={swipeableRef}
      renderLeftActions={renderLeftActions}
      renderRightActions={renderRightActions}
      leftThreshold={80}
      rightThreshold={80}
      friction={1.5}
    >
      <View style={{
        padding: 15,
        backgroundColor: "white",
        borderBottomWidth: 1,
        borderBottomColor: "#eee"
      }}>
        <Text>{item.title}</Text>
      </View>
    </Swipeable>
  );
}

Pressable

Modern pressable component using the new gesture API, providing advanced press handling with gesture coordination.

/**
 * Modern pressable component using the new gesture API
 * Provides sophisticated press detection with gesture relationship management
 */
function Pressable(props: {
  children?: React.ReactNode | ((state: PressableStateCallbackType) => React.ReactNode);
  onPress?: (event?: GestureResponderEvent) => void;
  onPressIn?: (event?: GestureResponderEvent) => void;
  onPressOut?: (event?: GestureResponderEvent) => void;
  onLongPress?: (event?: GestureResponderEvent) => void;
  onHoverIn?: (event?: MouseEvent) => void;
  onHoverOut?: (event?: MouseEvent) => void;
  disabled?: boolean;
  delayLongPress?: number;
  hitSlop?: Insets;
  pressRetentionOffset?: Insets;
  android_disableSound?: boolean;
  android_ripple?: AndroidRippleConfig;
  testID?: string;
  style?: StyleProp<ViewStyle> | ((state: PressableStateCallbackType) => StyleProp<ViewStyle>);
  unstable_pressDelay?: number;
  // Gesture Handler specific props
  simultaneousWithExternalGesture?: GestureRef[];
  requireExternalGestureToFail?: GestureRef[];
  blocksExternalGesture?: GestureRef[];
}): JSX.Element;

interface PressableProps {
  children?: React.ReactNode | ((state: PressableStateCallbackType) => React.ReactNode);
  onPress?: (event?: GestureResponderEvent) => void;
  onPressIn?: (event?: GestureResponderEvent) => void;
  onPressOut?: (event?: GestureResponderEvent) => void;
  onLongPress?: (event?: GestureResponderEvent) => void;
  onHoverIn?: (event?: MouseEvent) => void;
  onHoverOut?: (event?: MouseEvent) => void;
  disabled?: boolean;
  delayLongPress?: number;
  hitSlop?: Insets;
  pressRetentionOffset?: Insets;
  android_disableSound?: boolean;
  android_ripple?: AndroidRippleConfig;
  testID?: string;
  style?: StyleProp<ViewStyle> | ((state: PressableStateCallbackType) => StyleProp<ViewStyle>);
  unstable_pressDelay?: number;
  simultaneousWithExternalGesture?: GestureRef[];
  requireExternalGestureToFail?: GestureRef[];
  blocksExternalGesture?: GestureRef[];
}

interface PressableStateCallbackType {
  pressed: boolean;
  hovered?: boolean;
  focused?: boolean;
}

interface AndroidRippleConfig {
  color?: string;
  borderless?: boolean;
  radius?: number;
  foreground?: boolean;
}

Usage Example:

import React from "react";
import { Text, View } from "react-native";
import { Pressable } from "react-native-gesture-handler";

function MyPressable() {
  return (
    <Pressable
      onPress={() => console.log("Pressable pressed")}
      onLongPress={() => console.log("Pressable long pressed")}
      android_ripple={{ color: "rgba(0,0,0,0.1)", borderless: false }}
      style={({ pressed }) => [
        {
          padding: 15,
          backgroundColor: pressed ? "#e0e0e0" : "white",
          borderRadius: 8,
          borderWidth: 1,
          borderColor: "#ccc",
        },
      ]}
    >
      {({ pressed }) => (
        <Text style={{ color: pressed ? "blue" : "black" }}>
          {pressed ? "Pressed!" : "Press me"}
        </Text>
      )}
    </Pressable>
  );
}

DrawerLayout (Deprecated)

Drawer layout component for creating side navigation panels. This component is deprecated in favor of the react-native-reanimated version.

/**
 * Drawer layout component (deprecated)
 * Use react-native-reanimated drawer implementations instead
 * @deprecated Use DrawerLayout from react-native-reanimated
 */
function DrawerLayout(props: {
  drawerPosition: DrawerPosition;
  drawerWidth?: number;
  drawerBackgroundColor?: string;
  drawerLockMode?: DrawerLockMode;
  keyboardDismissMode?: DrawerKeyboardDismissMode;
  onDrawerClose?: () => void;
  onDrawerOpen?: () => void;
  onDrawerSlide?: (event: DrawerSlideEvent) => void;
  onDrawerStateChanged?: (newState: DrawerState) => void;
  renderNavigationView: () => React.ReactNode;
  statusBarBackgroundColor?: string;
  drawerType?: DrawerType;
  overlayColor?: string;
  contentContainerStyle?: StyleProp<ViewStyle>;
  edgeWidth?: number;
  minSwipeDistance?: number;
  hideStatusBar?: boolean;
  statusBarAnimation?: "slide" | "fade" | "none";
  children?: React.ReactNode;
}): JSX.Element;

interface DrawerLayoutProps {
  drawerPosition: DrawerPosition;
  drawerWidth?: number;
  drawerBackgroundColor?: string;
  drawerLockMode?: DrawerLockMode;
  keyboardDismissMode?: DrawerKeyboardDismissMode;
  onDrawerClose?: () => void;
  onDrawerOpen?: () => void;
  onDrawerSlide?: (event: DrawerSlideEvent) => void;
  onDrawerStateChanged?: (newState: DrawerState) => void;
  renderNavigationView: () => React.ReactNode;
  statusBarBackgroundColor?: string;
  drawerType?: DrawerType;
  overlayColor?: string;
  contentContainerStyle?: StyleProp<ViewStyle>;
  edgeWidth?: number;
  minSwipeDistance?: number;
  hideStatusBar?: boolean;
  statusBarAnimation?: "slide" | "fade" | "none";
  children?: React.ReactNode;
}

// Drawer-related types
type DrawerPosition = "left" | "right";
type DrawerState = "Idle" | "Dragging" | "Settling";
type DrawerType = "front" | "back" | "slide";
type DrawerLockMode = "unlocked" | "locked-closed" | "locked-open";
type DrawerKeyboardDismissMode = "none" | "on-drag";

interface DrawerSlideEvent {
  nativeEvent: {
    offset: number;
  };
}

Utility Components

createNativeWrapper

Function for wrapping React Native components to add gesture handler capabilities.

/**
 * Creates a native wrapper for React Native components
 * Adds gesture handling capabilities to existing components
 */
function createNativeWrapper<T>(
  Component: React.ComponentType<T>,
  config?: {
    shouldCancelWhenOutside?: boolean;
    shouldActivateOnStart?: boolean;
    disallowInterruption?: boolean;
  }
): React.ComponentType<T & {
  waitFor?: React.Ref<any> | React.Ref<any>[];
  simultaneousHandlers?: React.Ref<any> | React.Ref<any>[];
}>;

Usage Example:

import React from "react";
import { View } from "react-native";
import { createNativeWrapper } from "react-native-gesture-handler";

const WrappedView = createNativeWrapper(View, {
  shouldCancelWhenOutside: true,
  shouldActivateOnStart: true,
});

function MyWrappedView() {
  return (
    <WrappedView
      style={{ width: 100, height: 100, backgroundColor: "red" }}
      onGestureEvent={(event) => {
        console.log("Gesture event:", event.nativeEvent);
      }}
    />
  );
}

Advanced Animation Integration

Reanimated Integration

Advanced components work seamlessly with react-native-reanimated for smooth animations:

import React from "react";
import { View } from "react-native";
import Animated, { 
  useSharedValue, 
  useAnimatedStyle, 
  withSpring 
} from "react-native-reanimated";
import { Pressable } from "react-native-gesture-handler";

function AnimatedPressable() {
  const scale = useSharedValue(1);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  return (
    <Pressable
      onPressIn={() => {
        scale.value = withSpring(0.95);
      }}
      onPressOut={() => {
        scale.value = withSpring(1);
      }}
    >
      <Animated.View style={[{ padding: 20, backgroundColor: "blue" }, animatedStyle]}>
        {/* Content */}
      </Animated.View>
    </Pressable>
  );
}

Swipeable Animation Customization

Customize Swipeable animations with detailed control:

import React from "react";
import { View, Text, Animated } from "react-native";
import { Swipeable } from "react-native-gesture-handler";

function CustomAnimatedSwipeable() {
  const renderRightActions = (progress, dragX) => {
    // Custom scaling animation
    const scale = progress.interpolate({
      inputRange: [0, 1],
      outputRange: [0.8, 1],
    });

    // Custom opacity animation
    const opacity = progress.interpolate({
      inputRange: [0, 0.5, 1],
      outputRange: [0, 0.5, 1],
    });

    return (
      <Animated.View
        style={{
          flex: 1,
          backgroundColor: "red",
          justifyContent: "center",
          alignItems: "center",
          transform: [{ scale }],
          opacity,
        }}
      >
        <Text style={{ color: "white", fontWeight: "bold" }}>Delete</Text>
      </Animated.View>
    );
  };

  return (
    <Swipeable
      renderRightActions={renderRightActions}
      friction={1.5}
      rightThreshold={40}
    >
      <View style={{ padding: 20, backgroundColor: "white" }}>
        <Text>Swipe left for custom animation</Text>
      </View>
    </Swipeable>
  );
}

Performance Considerations

Memory Management

Advanced components are optimized for memory efficiency:

  • Automatic cleanup of gesture recognizers
  • Efficient animation value management
  • Reduced memory allocation during gestures

Native Performance

Components leverage native implementations for optimal performance:

  • Native thread gesture recognition
  • Hardware-accelerated animations
  • Minimal JavaScript bridge usage during interactions

Best Practices

// ✅ Good: Use refs for programmatic control
const swipeableRef = useRef<Swipeable>(null);

// ✅ Good: Cleanup in useEffect
useEffect(() => {
  return () => {
    swipeableRef.current?.close();
  };
}, []);

// ❌ Avoid: Creating new objects in render
const renderActions = () => ({ flex: 1 }); // Creates new object each render

// ✅ Better: Use static styles or useMemo
const actionStyles = { flex: 1 };
const renderActions = () => actionStyles;

Install with Tessl CLI

npx tessl i tessl/npm-react-native-gesture-handler

docs

advanced-components.md

buttons.md

components.md

constants.md

events.md

gestures.md

index.md

setup.md

tile.json