From 105bf9a59a4de9c7bf63868d225a50a3f403d3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Tue, 10 Feb 2026 12:24:46 +0100 Subject: [PATCH 1/7] migration guide --- .../docs/guides/upgrading-to-3.mdx | 11 ++++++++--- packages/react-native-gesture-handler/src/index.ts | 2 +- packages/react-native-gesture-handler/src/v3/index.ts | 2 ++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx b/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx index d8515f3727..830786ffe6 100644 --- a/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx +++ b/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx @@ -3,11 +3,10 @@ id: upgrading-to-3 title: Upgrading to the new API introduced in Gesture Handler 3 --- -## Migrating gestures - - import CodeComparison from '@site/src/components/CodeComparison'; +## Migrating gestures + The most important change brought by the Gesture Handler 3 is the new hook API. Migration is pretty straightforward. Instead of calling builder methods, everything is passed as a configuration object. +### createNativeWrapper + +`createNativeWrapper` has been rewritten using the new hook API and exported under the original name. The old implementation is still available as `legacy_createNativeWrapper`. It also accepts new optional parameter - `detectorType`, which allows you to specify the type of the [gesture detector](/docs/fundamentals/gesture-detectors) that will be used internally. By default it uses `GestureDetector`. + +While new `createNativeWrapper` should work out of the box, keep in mind that it wraps your component with `GestureDetector`, which in Gesture Handler 3 is a host component. This affects view hierarchy, so depending on your use case, you might want to use [`VirtualGestureDetector`](/docs/fundamentals/gesture-detectors#virtualgesturedetector) instead. To do that, simply pass the desired detector type as the second parameter of `createNativeWrapper`. + ## Replaced types Most of the types, like `TapGesture`, are still present in Gesture Handler 3. However, they are now used in new hook API. Types for old API now have `Legacy` prefix, e.g. `TapGesture` becomes `LegacyTapGesture`. diff --git a/packages/react-native-gesture-handler/src/index.ts b/packages/react-native-gesture-handler/src/index.ts index 7c601670c6..0535b27ed1 100644 --- a/packages/react-native-gesture-handler/src/index.ts +++ b/packages/react-native-gesture-handler/src/index.ts @@ -52,7 +52,7 @@ export { PanGestureHandler } from './handlers/PanGestureHandler'; export { PinchGestureHandler } from './handlers/PinchGestureHandler'; export { RotationGestureHandler } from './handlers/RotationGestureHandler'; export { FlingGestureHandler } from './handlers/FlingGestureHandler'; -export { default as createNativeWrapper } from './handlers/createNativeWrapper'; +export { default as legacy_createNativeWrapper } from './handlers/createNativeWrapper'; export type { NativeViewGestureHandlerProps } from './handlers/NativeViewGestureHandler'; export { GestureDetector as LegacyGestureDetector } from './handlers/gestures/GestureDetector'; export { GestureObjects as Gesture } from './handlers/gestures/gestureObjects'; diff --git a/packages/react-native-gesture-handler/src/v3/index.ts b/packages/react-native-gesture-handler/src/v3/index.ts index 835113c66d..fde89ad4e1 100644 --- a/packages/react-native-gesture-handler/src/v3/index.ts +++ b/packages/react-native-gesture-handler/src/v3/index.ts @@ -75,3 +75,5 @@ export { export type { ComposedGesture } from './types'; export { GestureStateManager } from './gestureStateManager'; + +export { default as createNativeWrapper } from './createNativeWrapper'; From b0f48689b166876a33048cea60c40393333abc5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Tue, 10 Feb 2026 12:32:07 +0100 Subject: [PATCH 2/7] Update skill --- skills/gesture-handler-3-migration/SKILL.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/skills/gesture-handler-3-migration/SKILL.md b/skills/gesture-handler-3-migration/SKILL.md index 6749d47e10..5e44605347 100644 --- a/skills/gesture-handler-3-migration/SKILL.md +++ b/skills/gesture-handler-3-migration/SKILL.md @@ -35,6 +35,7 @@ The exception to thait is `Gesture.ForceTouch` which DOES NOT have a counterpart #### Callback changes In Gesture Handler 3 some of the callbacks were renamed, namely: + - `onStart` -> `onActivate` - `onEnd` -> `onDeactivate` - `onTouchesCancelled` -> `onTouchesCancel` @@ -42,6 +43,7 @@ In Gesture Handler 3 some of the callbacks were renamed, namely: In the hooks API `onChange` is no longer available. Instead the `*change*` properties were moved to the event available inside `onUpdate`. All callbacks of a gesture are now using the same type: + - `usePanGesture()` -> `PanGestureEvent` - `useTapGesture()` -> `TapGestureEvent` - `useLongPressGesture()` -> `LongPressGestureEvent` @@ -53,6 +55,7 @@ All callbacks of a gesture are now using the same type: - `useManualGesture()` -> `ManualGestureEvent` The exception to this is touch events: + - `onTouchesDown` - `onTouchesUp` - `onTouchesMove` @@ -65,12 +68,14 @@ Where each callback receives `GestureTouchEvent` regardless of the hook used. In Gesture Handler 3, `stateManager` is no longer passed to `TouchEvent` callbacks. Instead, you should use the global `GestureStateManager`. `GestureStateManager` provides methods for imperative state management: + - .begin(handlerTag: number) - .activate(handlerTag: number) - .deactivate(handlerTag: number) (.end() in the old API) - .fail(handlerTag: number) `handlerTag` can be obtained in two ways: + 1. From the gesture object returned by the hook (`gesture.handlerTag`) 2. From the event inside callback (`event.handlerTag`) @@ -83,6 +88,7 @@ Callback definitions CANNOT reference the gesture that's being defined. In this `Gesture.Simultaneous(gesture1, gesture2);` becomes `useSimultaneousGestures(pan1, pan2);` All relations from the old API and their counterparts in the new one: + - `Gesture.Race()` -> `useCompetingGestures()` - `Gesture.Simultaneous()` -> `useSimultaneousGestures()` - `Gesture.Exclusive()` -> `useExclusiveGestures()` @@ -90,6 +96,7 @@ All relations from the old API and their counterparts in the new one: #### Cross components relations properties Properties used to define cross-components interactions were renamed: + - `.simultaneousWithExternalGesture` -> `simultaneousWith:` - `.requireExternalGestureToFail` -> `requireToFail:` - `.blocksExternalGesture` -> `block:` @@ -162,6 +169,12 @@ The implementation of buttons has been updated, resolving most button-related is Other components have also been internally rewritten using the new hook API but are exported under their original names, so no changes are necessary on your part. However, if you need to use the previous implementation for any reason, the legacy components are also available and are prefixed with `Legacy`, e.g., `ScrollView` is now available as `LegacyScrollView`. +`createNativeWrapper` has been rewritten using the new hook API and exported under the original name. The old implementation is still available as `legacy_createNativeWrapper`. It also accepts new optional parameter - `detectorType`, which allows you to specify the type of the gesture detector that will be used internally. By default it uses `GestureDetector`. + +While new `createNativeWrapper` should work out of the box, keep in mind that it wraps your component with `GestureDetector`, which in Gesture Handler 3 is a host component. This affects view hierarchy, so depending on use case, you might want to use `VirtualGestureDetector` instead. + +Before changing, ask user about their intention - if they prefer to keep legacy version, change it to `legacy_createNativeWrapper`. If not, keep `createNativeWrapper`, then notify user that in case of problems with view hierarchy they should introduce `InterceptingGestureDetector` and pass `GestureDetectorType.Virtual`as `detectorType` argument in `createNativeWrapper`. + ### Replaced types Most of the types used in the builder API, like `TapGesture`, are still present in Gesture Handler 3. However, they are now used in new hook API. Types for builder API now have `Legacy` prefix, e.g. `TapGesture` becomes `LegacyTapGesture`. From 3824142c63345614144ce368aa873fd9ffe2839f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Tue, 10 Feb 2026 15:17:53 +0100 Subject: [PATCH 3/7] Docs --- .../docs/components/create-native-wrapper.mdx | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 packages/docs-gesture-handler/docs/components/create-native-wrapper.mdx diff --git a/packages/docs-gesture-handler/docs/components/create-native-wrapper.mdx b/packages/docs-gesture-handler/docs/components/create-native-wrapper.mdx new file mode 100644 index 0000000000..415ee7295a --- /dev/null +++ b/packages/docs-gesture-handler/docs/components/create-native-wrapper.mdx @@ -0,0 +1,117 @@ +--- +id: create-native-wrapper +title: createNativeWrapper +sidebar_label: createNativeWrapper +--- + +`createNativeWrapper` is a function that lets you wrap components which are not provided by Gesture Handler with a [`Native`](/docs/gestures/use-native-gesture) gesture, allowing them to participate in the gesture recognition process. + +```tsx +import { Switch } from 'react-native'; +import { createNativeWrapper } from 'react-native-gesture-handler'; + +const RNGHSwitch = createNativeWrapper(Switch); +``` + +Full example can be seen in the [Example section](#example) below. + +This function can be useful when you want some third-party components to participate in gesture recognition process. + +## createNativeWrapper + +```ts +function createNativeWrapper

( + Component: React.ComponentType

, + config: NativeWrapperProperties, + detectorType: GestureDetectorType, +): React.FC

+``` + +[`config`](#config) and [`detectorType`](#detectortype) parameters are optional. Their default values are described in their respective sections below. + +This function returns original component wrapped with a specified [`GestureDetector`](/docs/fundamentals/gesture-detectors) that has a [`Native`](/docs/gestures/use-native-gesture) gesture attached to it: + +```tsx + + + +``` + +### Component + +Component to be wrapped with `Native` gesture. It can be any React component, including those from third-party libraries. + +### config + +Configuration for the `Native` gesture that will be attached to the wrapped component. For more details on available options, see the `Native` gesture [configuration](/docs/gestures/use-native-gesture#config) documentation. Defaults to an empty object. + +### detectorType + +```ts +enum GestureDetectorType { + Native, + Virtual, + Intercepting, +} +``` + +Type of the gesture detector that will be used to recognize the `Native` gesture. For more details on available options, see the [Gesture Detectors](/docs/fundamentals/gesture-detectors) documentation. Defaults to `GestureDetectorType.Native` (which is just [`GestureDetector`](/docs/fundamentals/gesture-detectors#gesture-detector)). + +## Components wrapped with createNativeWrapper + +Some of Gesture Handler's components are already wrapped with `createNativeWrapper` by default. These include: + +- `Switch` +- `TextInput` +- `ScrollView` +- `FlatList` +- `RefreshControl` +- `RawButton` +- `BaseButton` +- `RectButton` +- `BorderlessButton` + +## Example + +This example only demonstrates the usage of `createNativeWrapper`. `Switch` component from Gesture Handler comes wrapped with `createNativeWrapper` by default. + +```tsx +import { useState } from 'react'; +import { Switch } from 'react-native'; +import { + GestureDetector, + GestureHandlerRootView, + useTapGesture, + createNativeWrapper, +} from 'react-native-gesture-handler'; + +const RNGHSwitch = createNativeWrapper(Switch); + +export default function App() { + const [isEnabled, setIsEnabled] = useState(false); + + const tap1 = useTapGesture({ + onDeactivate: () => { + console.log('Tapped!'); + }, + }); + + const tap2 = useTapGesture({ + onDeactivate: () => { + console.log('Tapped!'); + }, + }); + + return ( + + + + + + + + + ); +} +``` +In this scenario, the `Switch` from React Native cannot be toggled on because the `tap1` gesture intercepts it. However, when wrapped with `createNativeWrapper`, the `RNGHSwitch` becomes capable of participating in the gesture recognition process. This setup allows the switch to be toggled on while still enabling `tap2` to recognize taps on it. \ No newline at end of file From c9924987614caf6b20e0a42c82661d34a0b1d4fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Tue, 10 Feb 2026 15:45:16 +0100 Subject: [PATCH 4/7] Mention dedicated section --- packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx b/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx index 830786ffe6..6ca7fc2836 100644 --- a/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx +++ b/packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx @@ -285,6 +285,8 @@ Other components have also been internally rewritten using the new hook API but While new `createNativeWrapper` should work out of the box, keep in mind that it wraps your component with `GestureDetector`, which in Gesture Handler 3 is a host component. This affects view hierarchy, so depending on your use case, you might want to use [`VirtualGestureDetector`](/docs/fundamentals/gesture-detectors#virtualgesturedetector) instead. To do that, simply pass the desired detector type as the second parameter of `createNativeWrapper`. +More on `createNativeWrapper` can be in the [dedicated section](/docs/components/create-native-wrapper) of the documentation. + ## Replaced types Most of the types, like `TapGesture`, are still present in Gesture Handler 3. However, they are now used in new hook API. Types for old API now have `Legacy` prefix, e.g. `TapGesture` becomes `LegacyTapGesture`. From 4f5438313194616c7be55d7e2dc969c99b8d9327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Tue, 10 Feb 2026 15:48:03 +0100 Subject: [PATCH 5/7] Update return type --- .../docs/components/create-native-wrapper.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs-gesture-handler/docs/components/create-native-wrapper.mdx b/packages/docs-gesture-handler/docs/components/create-native-wrapper.mdx index 415ee7295a..97e464e699 100644 --- a/packages/docs-gesture-handler/docs/components/create-native-wrapper.mdx +++ b/packages/docs-gesture-handler/docs/components/create-native-wrapper.mdx @@ -24,7 +24,7 @@ function createNativeWrapper

( Component: React.ComponentType

, config: NativeWrapperProperties, detectorType: GestureDetectorType, -): React.FC

+): React.FC

``` [`config`](#config) and [`detectorType`](#detectortype) parameters are optional. Their default values are described in their respective sections below. From 37b4e2f2403c88fdb8e190aad889b11ebfd85193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Tue, 10 Feb 2026 15:53:09 +0100 Subject: [PATCH 6/7] Update skill --- skills/gesture-handler-3-migration/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/gesture-handler-3-migration/SKILL.md b/skills/gesture-handler-3-migration/SKILL.md index 5e44605347..f2908ecf2e 100644 --- a/skills/gesture-handler-3-migration/SKILL.md +++ b/skills/gesture-handler-3-migration/SKILL.md @@ -173,7 +173,7 @@ Other components have also been internally rewritten using the new hook API but While new `createNativeWrapper` should work out of the box, keep in mind that it wraps your component with `GestureDetector`, which in Gesture Handler 3 is a host component. This affects view hierarchy, so depending on use case, you might want to use `VirtualGestureDetector` instead. -Before changing, ask user about their intention - if they prefer to keep legacy version, change it to `legacy_createNativeWrapper`. If not, keep `createNativeWrapper`, then notify user that in case of problems with view hierarchy they should introduce `InterceptingGestureDetector` and pass `GestureDetectorType.Virtual`as `detectorType` argument in `createNativeWrapper`. +Before changing, ask user about their intention - if they prefer to keep legacy version, change it to `legacy_createNativeWrapper`. If not, keep `createNativeWrapper`, then notify user that in case of problems with view hierarchy they should wrap the relevant subtree with `InterceptingGestureDetector` and pass `GestureDetectorType.Virtual` as the `detectorType` argument in `createNativeWrapper`. ### Replaced types From 319914c80f73705dab8cfbc2401fca79d5943812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Thu, 12 Feb 2026 12:58:44 +0100 Subject: [PATCH 7/7] Mention onGestureUpdate --- .../docs/components/create-native-wrapper.mdx | 63 +++++++++++++++---- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/packages/docs-gesture-handler/docs/components/create-native-wrapper.mdx b/packages/docs-gesture-handler/docs/components/create-native-wrapper.mdx index 97e464e699..30dbc80aee 100644 --- a/packages/docs-gesture-handler/docs/components/create-native-wrapper.mdx +++ b/packages/docs-gesture-handler/docs/components/create-native-wrapper.mdx @@ -4,7 +4,7 @@ title: createNativeWrapper sidebar_label: createNativeWrapper --- -`createNativeWrapper` is a function that lets you wrap components which are not provided by Gesture Handler with a [`Native`](/docs/gestures/use-native-gesture) gesture, allowing them to participate in the gesture recognition process. +`createNativeWrapper` is a function that lets you wrap components which are not provided by Gesture Handler with a [`Native gesture`](/docs/gestures/use-native-gesture), allowing them to participate in the gesture recognition process. ```tsx import { Switch } from 'react-native'; @@ -22,14 +22,14 @@ This function can be useful when you want some third-party components to partici ```ts function createNativeWrapper

( Component: React.ComponentType

, - config: NativeWrapperProperties, - detectorType: GestureDetectorType, + config: Readonly = {}, + detectorType: GestureDetectorType = GestureDetectorType.Native, ): React.FC

``` [`config`](#config) and [`detectorType`](#detectortype) parameters are optional. Their default values are described in their respective sections below. -This function returns original component wrapped with a specified [`GestureDetector`](/docs/fundamentals/gesture-detectors) that has a [`Native`](/docs/gestures/use-native-gesture) gesture attached to it: +This function returns original component wrapped with a specified [`GestureDetector`](/docs/fundamentals/gesture-detectors) that has a [`Native gesture`](/docs/gestures/use-native-gesture) attached to it: ```tsx @@ -39,11 +39,11 @@ This function returns original component wrapped with a specified [`GestureDetec ### Component -Component to be wrapped with `Native` gesture. It can be any React component, including those from third-party libraries. +Component to be wrapped with `Native gesture`. It can be any React component, including those from third-party libraries. ### config -Configuration for the `Native` gesture that will be attached to the wrapped component. For more details on available options, see the `Native` gesture [configuration](/docs/gestures/use-native-gesture#config) documentation. Defaults to an empty object. +Configuration for the `Native gesture` that will be attached to the wrapped component. For more details on available options, see the `Native gesture` [configuration](/docs/gestures/use-native-gesture#config) documentation. Defaults to an empty object. ### detectorType @@ -55,21 +55,58 @@ enum GestureDetectorType { } ``` -Type of the gesture detector that will be used to recognize the `Native` gesture. For more details on available options, see the [Gesture Detectors](/docs/fundamentals/gesture-detectors) documentation. Defaults to `GestureDetectorType.Native` (which is just [`GestureDetector`](/docs/fundamentals/gesture-detectors#gesture-detector)). +Type of the gesture detector that will be used to recognize the `Native gesture`. For more details on available options, see the [Gesture Detectors](/docs/fundamentals/gesture-detectors) documentation. Defaults to `GestureDetectorType.Native` (which is just [`GestureDetector`](/docs/fundamentals/gesture-detectors#gesture-detector)). -## Components wrapped with createNativeWrapper +## onGestureUpdate_CAN_CAUSE_INFINITE_RERENDER -Some of Gesture Handler's components are already wrapped with `createNativeWrapper` by default. These include: +:::danger +This callback may lead to infinite re-renders if not used carefully. +::: + +```ts +onGestureUpdate_CAN_CAUSE_INFINITE_RERENDER?: (gesture: NativeGesture) => void; +``` + +Components wrapped with `createNativeWrapper` receive an additional prop named `onGestureUpdate_CAN_CAUSE_INFINITE_RERENDER`. This callback is triggered on every update of the `Native gesture` associated with the wrapped component, providing access to the underlying gesture. This can be helpful when setting up [relations](/docs/fundamentals/gesture-composition) with other gestures. + + +To prevent infinite re-renders, ensure you are not updating the component with the same gesture repeatedly, e.g.: + +```tsx +const WrappedComponent = createNativeWrapper(Component); + +const ParentComponent = () => { + const [nativeGesture, setNativeGesture] = useState( + null + ); + + const onGestureUpdate = (gesture: NativeGesture) => { + if (!nativeGesture || nativeGesture.handlerTag !== gesture.handlerTag) { + setNativeGesture(gesture); + ... + } + }; +}; + +; +``` + +You can also check example usage in our [`ScrollView` component](https://github.com/software-mansion/react-native-gesture-handler/blob/18af65aa7d1d425cecbdba3224271246ea739132/packages/react-native-gesture-handler/src/v3/components/GestureComponents.tsx#L80). + +## Components wrapped using createNativeWrapper + +Gesture Handler reexports some of the React Native components that are already wrapped using `createNativeWrapper`. These include: - `Switch` - `TextInput` - `ScrollView` - `FlatList` - `RefreshControl` -- `RawButton` -- `BaseButton` -- `RectButton` -- `BorderlessButton` + +[Buttons](/docs/components/buttons) are also wrapped using `createNativeWrapper` by default. ## Example