Advanced hooks and utilities for customizing gesture behavior, scroll event handling, and coordinating interactions between bottom sheet components and scrollable content.
Default gesture event handlers that provide comprehensive pan gesture behavior including drag physics, over-drag resistance, and snap point calculations.
/**
* Hook providing default gesture event handlers for pan gestures
* Handles drag behavior, over-drag resistance, snap points, and keyboard interactions
* @returns Object with gesture event handler callbacks
*/
declare function useGestureEventsHandlersDefault(): GestureEventHandlers;
type GestureEventsHandlersHookType = () => {
/** Called when gesture starts */
handleOnStart: GestureEventHandlerCallbackType;
/** Called during gesture movement */
handleOnChange: GestureEventHandlerCallbackType;
/** Called when gesture ends */
handleOnEnd: GestureEventHandlerCallbackType;
/** Called when gesture is finalized/cleaned up */
handleOnFinalize: GestureEventHandlerCallbackType;
};
type GestureEventHandlerCallbackType = (
source: GESTURE_SOURCE,
payload: GestureEventPayloadType
) => void;
enum GESTURE_SOURCE {
UNDETERMINED = 0,
SCROLLABLE = 1,
HANDLE = 2,
CONTENT = 3,
}Usage Examples:
import React from 'react';
import { View, Text } from 'react-native';
import BottomSheet, { useGestureEventsHandlersDefault } from '@gorhom/bottom-sheet';
// Using default gesture handlers (automatic - no code needed)
const DefaultGestureExample = () => {
const snapPoints = useMemo(() => ['25%', '50%', '75%'], []);
return (
<BottomSheet snapPoints={snapPoints}>
<View style={{ flex: 1, padding: 20 }}>
<Text>Uses default gesture handling automatically</Text>
</View>
</BottomSheet>
);
};
// Custom gesture handlers extending default behavior
const CustomGestureExample = () => {
const snapPoints = useMemo(() => ['25%', '50%', '75%'], []);
const customGestureHandlers = () => {
const defaultHandlers = useGestureEventsHandlersDefault();
return {
handleOnStart: (source, payload) => {
console.log('Custom gesture start', source);
defaultHandlers.handleOnStart(source, payload);
},
handleOnChange: (source, payload) => {
// Custom logic during drag
if (payload.translationY < -100) {
console.log('Dragging up significantly');
}
defaultHandlers.handleOnChange(source, payload);
},
handleOnEnd: (source, payload) => {
console.log('Custom gesture end', source);
defaultHandlers.handleOnEnd(source, payload);
},
handleOnFinalize: (source, payload) => {
console.log('Custom gesture finalize');
defaultHandlers.handleOnFinalize(source, payload);
},
};
};
return (
<BottomSheet
snapPoints={snapPoints}
gestureEventsHandlersHook={customGestureHandlers}
>
<View style={{ flex: 1, padding: 20 }}>
<Text>Uses custom gesture handling with logging</Text>
</View>
</BottomSheet>
);
};Default scroll event handlers that coordinate scrolling behavior with sheet position, managing scroll locking and position interactions.
/**
* Hook providing default scroll event handlers
* Coordinates scrolling with sheet position, handles scroll locking
* @param scrollableRef - Reference to the scrollable component
* @param scrollableContentOffsetY - Shared value tracking scroll position
* @returns Object with scroll event handler callbacks
*/
declare function useScrollEventsHandlersDefault(
scrollableRef: RefObject<Scrollable>,
scrollableContentOffsetY: SharedValue<number>
): ScrollEventHandlers;
type ScrollEventsHandlersHookType = (
ref: React.RefObject<Scrollable>,
contentOffsetY: SharedValue<number>
) => ScrollEventHandlers;
interface ScrollEventHandlers {
/** Called during scroll events */
handleOnScroll?: ScrollEventHandlerCallbackType;
/** Called when scroll begins by user drag */
handleOnBeginDrag?: ScrollEventHandlerCallbackType;
/** Called when scroll ends */
handleOnEndDrag?: ScrollEventHandlerCallbackType;
/** Called when momentum scrolling begins */
handleOnMomentumBegin?: ScrollEventHandlerCallbackType;
/** Called when momentum scrolling ends */
handleOnMomentumEnd?: ScrollEventHandlerCallbackType;
}
type ScrollEventHandlerCallbackType<C = never> = (
payload: NativeScrollEvent,
context: C
) => void;
type Scrollable = FlatList | ScrollView | SectionList;Usage Examples:
import React, { useRef } from 'react';
import { View, Text } from 'react-native';
import BottomSheet, {
BottomSheetScrollView,
useScrollEventsHandlersDefault
} from '@gorhom/bottom-sheet';
// Custom scroll event handling
const CustomScrollExample = () => {
const snapPoints = useMemo(() => ['25%', '50%', '75%'], []);
const customScrollHandlers = (scrollableRef, contentOffsetY) => {
const defaultHandlers = useScrollEventsHandlersDefault(
scrollableRef,
contentOffsetY
);
return {
handleOnScroll: (payload, context) => {
console.log('Scroll position:', payload.contentOffset.y);
defaultHandlers.handleOnScroll?.(payload, context);
},
handleOnBeginDrag: (payload, context) => {
console.log('User started scrolling');
defaultHandlers.handleOnBeginDrag?.(payload, context);
},
handleOnEndDrag: (payload, context) => {
console.log('User stopped scrolling');
defaultHandlers.handleOnEndDrag?.(payload, context);
},
handleOnMomentumEnd: (payload, context) => {
console.log('Momentum scrolling ended');
defaultHandlers.handleOnMomentumEnd?.(payload, context);
},
};
};
return (
<BottomSheet snapPoints={snapPoints}>
<BottomSheetScrollView
scrollEventsHandlersHook={customScrollHandlers}
contentContainerStyle={{ padding: 20 }}
>
{Array.from({ length: 20 }).map((_, index) => (
<View key={index} style={{ padding: 10, borderBottomWidth: 1 }}>
<Text>Item {index + 1}</Text>
</View>
))}
</BottomSheetScrollView>
</BottomSheet>
);
};
// Scroll position monitoring
const ScrollMonitorExample = () => {
const snapPoints = useMemo(() => ['30%', '60%', '90%'], []);
const [scrollPosition, setScrollPosition] = useState(0);
const monitoringScrollHandlers = (scrollableRef, contentOffsetY) => {
const defaultHandlers = useScrollEventsHandlersDefault(
scrollableRef,
contentOffsetY
);
return {
...defaultHandlers,
handleOnScroll: (payload, context) => {
setScrollPosition(payload.contentOffset.y);
defaultHandlers.handleOnScroll?.(payload, context);
},
};
};
return (
<BottomSheet snapPoints={snapPoints}>
<View style={{ padding: 20 }}>
<Text>Scroll Position: {scrollPosition.toFixed(2)}</Text>
</View>
<BottomSheetScrollView
scrollEventsHandlersHook={monitoringScrollHandlers}
contentContainerStyle={{ padding: 20 }}
>
{/* Content */}
</BottomSheetScrollView>
</BottomSheet>
);
};Provides access to configured gesture handlers for both content and handle pan gestures.
/**
* Hook providing access to configured gesture handlers
* Must be used within BottomSheet context
* @returns Pre-configured gesture handlers for content and handle
*/
declare function useBottomSheetGestureHandlers(): BottomSheetGestureHandlersContextType;
interface BottomSheetGestureHandlersContextType {
/** Gesture handler for content pan gestures */
contentPanGestureHandler: ReturnType<GestureHandlersHookType>;
/** Gesture handler for handle pan gestures */
handlePanGestureHandler: ReturnType<GestureHandlersHookType>;
}
type GestureHandlersHookType = (
source: GESTURE_SOURCE,
state: SharedValue<State>,
gestureSource: SharedValue<GESTURE_SOURCE>,
onStart: GestureEventHandlerCallbackType,
onChange: GestureEventHandlerCallbackType,
onEnd: GestureEventHandlerCallbackType,
onFinalize: GestureEventHandlerCallbackType
) => GestureHandlerCallbacks;Usage Example:
import React from 'react';
import { View, Text } from 'react-native';
import BottomSheet, { useBottomSheetGestureHandlers } from '@gorhom/bottom-sheet';
const GestureHandlersExample = () => {
const CustomComponent = () => {
const { contentPanGestureHandler, handlePanGestureHandler } =
useBottomSheetGestureHandlers();
return (
<View style={{ flex: 1, padding: 20 }}>
<Text>Component with access to gesture handlers</Text>
{/* Custom gesture coordination logic */}
</View>
);
};
return (
<BottomSheet snapPoints={['50%', '80%']}>
<CustomComponent />
</BottomSheet>
);
};Creates an animated scroll handler that integrates scrollable components with the bottom sheet gesture system.
/**
* Creates animated scroll handler for scrollable component integration
* @param useScrollEventsHandlers - Custom scroll events handler hook (optional)
* @param onScroll - Additional scroll event callback
* @param onScrollBeginDrag - Additional scroll begin drag callback
* @param onScrollEndDrag - Additional scroll end drag callback
* @returns Scroll handler components and refs
*/
declare function useScrollHandler(
useScrollEventsHandlers?: ScrollEventsHandlersHookType,
onScroll?: ScrollableEvent,
onScrollBeginDrag?: ScrollableEvent,
onScrollEndDrag?: ScrollableEvent
): ScrollHandlerResult;
interface ScrollHandlerResult {
/** Animated scroll handler for the scrollable component */
scrollHandler: ReturnType<typeof useAnimatedScrollHandler>;
/** Reference to the scrollable component */
scrollableRef: RefObject<Scrollable>;
/** Shared value tracking scroll content offset Y */
scrollableContentOffsetY: SharedValue<number>;
}
type ScrollableEvent = (
event: Pick<NativeSyntheticEvent<NativeScrollEvent>, 'nativeEvent'>
) => void;Usage Examples:
import React, { useCallback } from 'react';
import { ScrollView, Text, View } from 'react-native';
import BottomSheet, { useScrollHandler } from '@gorhom/bottom-sheet';
// Basic scroll handler usage
const BasicScrollHandlerExample = () => {
const snapPoints = useMemo(() => ['30%', '60%', '90%'], []);
const CustomScrollableComponent = () => {
const { scrollHandler, scrollableRef, scrollableContentOffsetY } =
useScrollHandler();
return (
<ScrollView
ref={scrollableRef}
onScroll={scrollHandler}
scrollEventThrottle={16}
contentContainerStyle={{ padding: 20 }}
>
{Array.from({ length: 30 }).map((_, index) => (
<View key={index} style={{ padding: 10, borderBottomWidth: 1 }}>
<Text>Item {index + 1}</Text>
</View>
))}
</ScrollView>
);
};
return (
<BottomSheet snapPoints={snapPoints}>
<CustomScrollableComponent />
</BottomSheet>
);
};
// With additional scroll callbacks
const ScrollHandlerWithCallbacksExample = () => {
const snapPoints = useMemo(() => ['25%', '50%', '75%'], []);
const CustomScrollableComponent = () => {
const handleScroll = useCallback((event) => {
console.log('Additional scroll handling:', event.nativeEvent.contentOffset.y);
}, []);
const handleScrollBeginDrag = useCallback((event) => {
console.log('Additional begin drag handling');
}, []);
const handleScrollEndDrag = useCallback((event) => {
console.log('Additional end drag handling');
}, []);
const { scrollHandler, scrollableRef } = useScrollHandler(
undefined, // Use default scroll events handlers
handleScroll,
handleScrollBeginDrag,
handleScrollEndDrag
);
return (
<ScrollView
ref={scrollableRef}
onScroll={scrollHandler}
scrollEventThrottle={16}
contentContainerStyle={{ padding: 20 }}
>
{/* Content */}
</ScrollView>
);
};
return (
<BottomSheet snapPoints={snapPoints}>
<CustomScrollableComponent />
</BottomSheet>
);
};Registers a scrollable component with the bottom sheet's internal state management system for proper gesture coordination.
/**
* Registers scrollable component with bottom sheet state management
* Called internally by scrollable components to register themselves
* @param ref - Reference to the scrollable component
* @param type - Type of scrollable component
* @param contentOffsetY - Shared value for tracking scroll position
* @param refreshable - Whether the scrollable supports pull-to-refresh
* @param useFocusHook - Hook for focus events (default: useEffect)
*/
declare function useScrollableSetter(
ref: RefObject<Scrollable>,
type: SCROLLABLE_TYPE,
contentOffsetY: SharedValue<number>,
refreshable: boolean,
useFocusHook?: typeof useEffect
): void;
enum SCROLLABLE_TYPE {
UNDETERMINED = 0,
VIEW = 1,
FLATLIST = 2,
SCROLLVIEW = 3,
SECTIONLIST = 4,
VIRTUALIZEDLIST = 5,
}Usage Example:
import React, { useRef, useEffect } from 'react';
import { ScrollView } from 'react-native';
import { useFocusEffect } from '@react-navigation/native';
import { useScrollableSetter, SCROLLABLE_TYPE } from '@gorhom/bottom-sheet';
import { useSharedValue } from 'react-native-reanimated';
// Custom scrollable component using useScrollableSetter
const CustomScrollableComponent = ({ refreshable = false }) => {
const scrollViewRef = useRef<ScrollView>(null);
const contentOffsetY = useSharedValue(0);
// Register with bottom sheet
useScrollableSetter(
scrollViewRef,
SCROLLABLE_TYPE.SCROLLVIEW,
contentOffsetY,
refreshable,
useEffect // Use useFocusEffect for React Navigation
);
return (
<ScrollView
ref={scrollViewRef}
// ... other props
>
{/* Content */}
</ScrollView>
);
};
// With React Navigation integration
const NavigationScrollableComponent = () => {
const scrollViewRef = useRef<ScrollView>(null);
const contentOffsetY = useSharedValue(0);
useScrollableSetter(
scrollViewRef,
SCROLLABLE_TYPE.SCROLLVIEW,
contentOffsetY,
false,
useFocusEffect // Proper React Navigation integration
);
return (
<ScrollView ref={scrollViewRef}>
{/* Content */}
</ScrollView>
);
};Creates a custom scrollable component creator for integrating third-party scrollable libraries with bottom sheet.
/**
* Creates custom scrollable component creator for third-party libraries
* @param configs - Configuration for scrollable creator
* @returns Function that creates bottom sheet integrated scrollable components
*/
declare function useBottomSheetScrollableCreator<T = any>({
focusHook,
scrollEventsHandlersHook,
enableFooterMarginAdjustment,
}: BottomSheetScrollableCreatorConfigs = {}): (props: T, ref?: never) => ReactElement<T>;
interface BottomSheetScrollableCreatorConfigs extends BottomSheetScrollableProps {
/** Focus hook for navigation integration (default: useEffect) */
focusHook?: typeof useEffect;
/** Custom scroll event handlers hook */
scrollEventsHandlersHook?: ScrollEventsHandlersHookType;
/** Adjust margin for animated footer (default: false) */
enableFooterMarginAdjustment?: boolean;
}Usage Examples:
import React from 'react';
import { View } from 'react-native';
import { useFocusEffect } from '@react-navigation/native';
import BottomSheet, { useBottomSheetScrollableCreator } from '@gorhom/bottom-sheet';
// Integration with third-party scrollable library
const ThirdPartyScrollableExample = () => {
const snapPoints = useMemo(() => ['40%', '70%'], []);
// Create integrated component for third-party library
const createScrollableComponent = useBottomSheetScrollableCreator({
focusHook: useFocusEffect,
enableFooterMarginAdjustment: true,
});
// Usage with a third-party library like LegendList
const renderScrollComponent = (props) => {
return createScrollableComponent(props);
};
return (
<BottomSheet snapPoints={snapPoints}>
<View style={{ flex: 1 }}>
{/* Use with third-party library */}
<SomeThirdPartyList
data={data}
renderScrollComponent={renderScrollComponent}
// ... other props
/>
</View>
</BottomSheet>
);
};
// FlashList integration example
const FlashListIntegrationExample = () => {
const snapPoints = useMemo(() => ['50%', '90%'], []);
const createFlashListComponent = useBottomSheetScrollableCreator({
scrollEventsHandlersHook: customScrollHandlers,
enableFooterMarginAdjustment: true,
});
return (
<BottomSheet snapPoints={snapPoints}>
<View style={{ flex: 1 }}>
{/* Integrate with FlashList or similar libraries */}
<CustomList
renderScrollComponent={createFlashListComponent}
data={data}
/>
</View>
</BottomSheet>
);
};The gesture event payload contains comprehensive information about the current gesture state:
interface GestureEventPayloadType {
/** Absolute X position */
absoluteX: number;
/** Absolute Y position */
absoluteY: number;
/** X position relative to view */
x: number;
/** Y position relative to view */
y: number;
/** X translation from gesture start */
translationX: number;
/** Y translation from gesture start */
translationY: number;
/** X velocity */
velocityX: number;
/** Y velocity */
velocityY: number;
/** Current gesture state */
state: State;
/** Number of pointers */
numberOfPointers: number;
}For advanced use cases, you can coordinate multiple gesture systems:
const AdvancedGestureCoordination = () => {
const customGestureHandlers = () => {
const defaultHandlers = useGestureEventsHandlersDefault();
return {
handleOnStart: (source, payload) => {
// Coordinate with other gesture systems
if (source === GESTURE_SOURCE.SCROLLABLE) {
console.log('Scroll gesture started');
} else if (source === GESTURE_SOURCE.HANDLE) {
console.log('Handle gesture started');
}
defaultHandlers.handleOnStart(source, payload);
},
// ... other handlers
};
};
return (
<BottomSheet
snapPoints={['25%', '50%', '75%']}
gestureEventsHandlersHook={customGestureHandlers}
// Fine-tune gesture recognition
activeOffsetY={[-1, 1]}
failOffsetX={[-5, 5]}
>
{/* Content */}
</BottomSheet>
);
};