Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
9fb1120
Base implementation
m-bert Mar 6, 2026
093630b
Merge branch 'main' into @mbert/clickable
m-bert Mar 6, 2026
f1134cb
Do not use animated button if not necessary
m-bert Mar 6, 2026
9461ecc
Use props
m-bert Mar 6, 2026
ab6ced1
Types
m-bert Mar 6, 2026
27b3d0c
Additional props
m-bert Mar 6, 2026
c0d4960
Working component
m-bert Mar 6, 2026
25ed517
Small refactor
m-bert Mar 6, 2026
5e4262b
Add example
m-bert Mar 6, 2026
5646775
Looks good
m-bert Mar 9, 2026
202a999
Unified, without ripple
m-bert Mar 9, 2026
dd2c3bf
Rippleeeee
m-bert Mar 9, 2026
2cf923b
clear activeState also in onFinalize
m-bert Mar 9, 2026
8059e40
Fix borderless
m-bert Mar 9, 2026
6b043a9
Default ripple color
m-bert Mar 9, 2026
1eb680e
Rename file
m-bert Mar 9, 2026
b2f7e05
Fix index
m-bert Mar 9, 2026
44bbcc6
Split component and types
m-bert Mar 9, 2026
f4d895e
Add presets
m-bert Mar 10, 2026
491c27a
Update gitignore
m-bert Mar 10, 2026
e404f8a
Renames
m-bert Mar 10, 2026
583e864
Remove preset
m-bert Mar 10, 2026
769adb4
Ripple
m-bert Mar 10, 2026
e4bb348
Example
m-bert Mar 10, 2026
96c016b
Rename in example
m-bert Mar 10, 2026
fe9f47f
Opacity fix
m-bert Mar 10, 2026
aca162b
JSDocs
m-bert Mar 10, 2026
c6f27e7
Vibe stress test
m-bert Mar 10, 2026
4200868
First refactor
m-bert Mar 10, 2026
beb6710
Second optimization
m-bert Mar 11, 2026
1a23339
Change props
m-bert Mar 11, 2026
0e5b735
Bring back use callback
m-bert Mar 11, 2026
ef24b2f
Merge branch 'main' into @mbert/clickable
m-bert Mar 11, 2026
d6113c6
Tests
m-bert Mar 12, 2026
ac02b27
Reset longpress outside of if
m-bert Mar 12, 2026
8864e89
Easier stress test
m-bert Mar 12, 2026
b74ce61
Deprecate old buttons
m-bert Mar 12, 2026
d422f33
Update SKILL
m-bert Mar 12, 2026
822ba83
Add onPressIn and onPressOut
m-bert Mar 12, 2026
db37209
Merge branch '@mbert/clickable' into @mbert/clickable-docs
m-bert Mar 12, 2026
ff74f2b
Docs
m-bert Mar 12, 2026
c9aba4d
Revert changes in button
m-bert Mar 13, 2026
f2b3bb9
Merge branch 'main' into @mbert/clickable
m-bert Mar 13, 2026
a9688b5
Merge branch '@mbert/clickable' into @mbert/clickable-docs
m-bert Mar 13, 2026
78d4273
update SKILL
m-bert Mar 13, 2026
050f122
Fix deprecation message for `RectButton` to reference `underlayActive…
m-bert Mar 16, 2026
270b111
Call onPressIn when e.pointerInsideis true
m-bert Mar 16, 2026
77d411d
Stress test example timeout cleanup
m-bert Mar 16, 2026
08e5e2e
Merge branch '@mbert/clickable' of github.com:software-mansion/react-…
m-bert Mar 16, 2026
d80e5cf
Colooooooors
m-bert Mar 16, 2026
95a887f
Update underlayColor description to require underlayActiveOpacity
m-bert Mar 16, 2026
25126cf
Add default values for underlayInitialOpacity and initialOpacity in C…
m-bert Mar 16, 2026
45ffb2f
Remove unnecessary border styles
m-bert Mar 16, 2026
6888721
Merge branch '@mbert/clickable' into @mbert/clickable-docs
m-bert Mar 16, 2026
a3b2c87
Minor adjustments
m-bert Mar 16, 2026
90197cf
Update onPressIn and onPressOut type definitions to include GestureEv…
m-bert Mar 16, 2026
bce2aa5
rephrase
m-bert Mar 16, 2026
cad279b
Copilot review
m-bert Mar 16, 2026
4fb5ab2
Merge branch 'main' into @mbert/clickable-docs
m-bert Apr 3, 2026
c21dfd3
Update props names
m-bert Apr 3, 2026
bafbc07
Merge branch 'main' into @mbert/clickable-docs
m-bert Apr 3, 2026
0f3528a
Change props order
m-bert Apr 3, 2026
8ef412f
Grammar
m-bert Apr 3, 2026
20f82bb
Correct prop in SKILL
m-bert Apr 3, 2026
ec80be4
RawButton deprecations
m-bert Apr 7, 2026
3db2cd1
Add replacement info to migration guide
m-bert Apr 7, 2026
f4c02fc
Update packages/docs-gesture-handler/docs/components/clickable.mdx
m-bert Apr 7, 2026
fed7981
Update packages/docs-gesture-handler/docs/components/clickable.mdx
m-bert Apr 7, 2026
8f336b4
Merge branch '@mbert/clickable-docs' of github.com:software-mansion/r…
m-bert Apr 7, 2026
a2ed828
Update packages/docs-gesture-handler/docs/components/clickable.mdx
m-bert Apr 7, 2026
2f074ce
Update packages/docs-gesture-handler/docs/components/clickable.mdx
m-bert Apr 7, 2026
224e02e
Update packages/docs-gesture-handler/docs/components/clickable.mdx
m-bert Apr 7, 2026
ef7020e
Update packages/docs-gesture-handler/docs/components/clickable.mdx
m-bert Apr 7, 2026
b0ed1d4
Review
m-bert Apr 7, 2026
5c6e5e8
Merge branch '@mbert/clickable-docs' of github.com:software-mansion/r…
m-bert Apr 7, 2026
cb43f81
Merge branch 'main' into @mbert/clickable-docs
m-bert Apr 7, 2026
d3c4337
Mimic Touchable
m-bert Apr 7, 2026
46eba22
Add Touchable migration to SKILL
m-bert Apr 8, 2026
aeaef39
Rename to Touchable
m-bert Apr 8, 2026
1cd3061
Other renames
m-bert Apr 8, 2026
5007490
Merge branch 'main' into @mbert/clickable-docs
m-bert Apr 8, 2026
2cee2c4
Touchable replacement note
m-bert Apr 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/docs-gesture-handler/docs/components/buttons.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import GifGallery from '@site/components/GifGallery';

import HeaderWithBadges from '@site/src/components/HeaderWithBadges';

:::danger
Button components described in this section are deprecated and will be removed in the future. Please use [`Touchable`](/docs/components/touchable) instead.
:::

<GifGallery>
<img src={useBaseUrl('gifs/samplebutton.gif')} width="280" />
</GifGallery>
Expand Down Expand Up @@ -37,7 +41,7 @@ The most basic button component does not provide any feedback and lacks props su
exclusive?: boolean;
```

Defines if more than one button could be pressed simultaneously. By default set to `true`.
Defines whether pressing this button prevents other buttons exported by Gesture Handler from being pressed. By default set to `true`.

<HeaderWithBadges platforms={['android']}>
### rippleColor
Expand Down Expand Up @@ -194,7 +198,7 @@ If you wish to get specific information about platforms design patterns, visit [

This library allows the use of native components with native feedback in adequate situations.

If you do not wish to implement a custom design approach, [`RectButton`](#rectbutton) and [`BorderlessButton`](#borderlessbutton) seem to be absolutely enough and there's no need to use anything else. In all the remaining cases, you can always rely on [`BaseButton`](#basebutton) which is a superclass for the other button classes and can be used as a generic `Touchable` replacement that can be customized to your needs.
If you do not wish to implement a custom design approach, [`RectButton`](#rectbutton) and [`BorderlessButton`](#borderlessbutton) seem to be absolutely enough and there's no need to use anything else. In all the remaining cases, you can always rely on [`BaseButton`](#basebutton) which is a superclass for the other button classes and can be used as a generic React Native `Touchable` replacement that can be customized to your needs.

Below we list some of the common usecases for button components to be used along with the type of button that should be used according to the platform specific design guidelines.

Expand Down
326 changes: 326 additions & 0 deletions packages/docs-gesture-handler/docs/components/touchable.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
---
id: touchable
title: Touchable
sidebar_label: Touchable
---

import HeaderWithBadges from '@site/src/components/HeaderWithBadges';

:::note
This section refers to new `Touchable` component, meant to replace both buttons and touchables. If you are looking for documentation for the deprecated touchable components, check out the [Legacy Touchables](/docs/components/legacy-touchables) section.
:::

`Touchable` is a versatile new component introduced in Gesture Handler 3 to supersede previous button implementations. Designed for maximum flexibility, it provides a highly customizable interface for native touch handling while ensuring consistent behavior across platforms.

`Touchable` provides a simple interface for the common animations like opacity, underlay, and scale, implemented entirely on the platform. On Android, it also exposes the native ripple effect on press (turned off by default).

If the provided animations are not sufficient, it's possible to use `Touchable` to create fully custom interactions using either [Reanimated](https://docs.swmansion.com/react-native-reanimated/) or [Animated API](https://reactnative.dev/docs/animated).

## Replacing old buttons

If you were using `RectButton` or `BorderlessButton` in your app, you should replace them with `Touchable`. Check out the full code in the [example](#example) section below.

### RectButton

To replace `RectButton` with `Touchable`, simply add `activeUnderlayOpacity={0.105}` to your `Touchable`. This will animate the underlay when the button is pressed.

```tsx
<Touchable
...
activeUnderlayOpacity={0.105}/>
```

### BorderlessButton

Replacing `BorderlessButton` with `Touchable` is as easy as replacing `RectButton`. Just add `activeOpacity={0.3}` to your `Touchable`. This will animate the whole component when the button is pressed.

```tsx
<Touchable
...
activeOpacity={0.3}/>
```

## Migrating from legacy Touchable variants

If you were using the specialized touchable components (`TouchableOpacity`, `TouchableHighlight`, `TouchableWithoutFeedback`, or `TouchableNativeFeedback`), you can replicate their behavior with the unified `Touchable` component.

### TouchableOpacity

To replace `TouchableOpacity`, add `activeOpacity={0.2}`.

```tsx
<Touchable
...
activeOpacity={0.2}/>
```

### TouchableHighlight

To replace `TouchableHighlight`, add `activeUnderlayOpacity={1}`.

```tsx
<Touchable
...
activeUnderlayOpacity={1}/>
```

### TouchableWithoutFeedback

To replace `TouchableWithoutFeedback`, use a plain `Touchable`.

```tsx
<Touchable ... />
```

### TouchableNativeFeedback

To replicate `TouchableNativeFeedback` behavior, use the [`androidRipple`](#androidripple) prop. Make sure to set `foreground={true}`.

```tsx
<Touchable
...
androidRipple={{
foreground: true,
}}/>
```

## Example

In this example we will demonstrate how to recreate `RectButton` and `BorderlessButton` effects using the `Touchable` component.

<CollapsibleCode
label="Show full example"
expandedLabel="Hide full example"
lineBounds={[7, 40]}
src={`
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import {
GestureHandlerRootView,
Touchable,
} from 'react-native-gesture-handler';

export default function TouchableExample() {
return (
<GestureHandlerRootView style={styles.container}>
<Touchable
onPress={() => {
console.log('BaseButton built with Touchable');
}}
style={[styles.button, { backgroundColor: '#7d63d9' }]}>
<Text style={styles.buttonText}>BaseButton</Text>
</Touchable>

<Touchable
onPress={() => {
console.log('RectButton built with Touchable');
}}
style={[styles.button, { backgroundColor: '#4f9a84' }]}
activeUnderlayOpacity={0.105}>
<Text style={styles.buttonText}>RectButton</Text>
</Touchable>

<Touchable
onPress={() => {
console.log('BorderlessButton built with Touchable');
}}
style={[styles.button, { backgroundColor: '#5f97c8' }]}
activeOpacity={0.3}>
<Text style={styles.buttonText}>BorderlessButton</Text>
</Touchable>
</GestureHandlerRootView>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',

gap: 20,
},
button: {
width: 200,
height: 70,
borderRadius: 15,
alignItems: 'center',
justifyContent: 'center',
},
buttonText: {
color: 'white',
fontSize: 14,
fontWeight: '600',
},
});
`}/>


## Properties

### activeOpacity

```ts
activeOpacity?: number;
```

Defines the opacity of the whole component when the button is active.

### defaultOpacity

```ts
defaultOpacity?: number;
```

Defines the opacity of the whole component when the button is active. By default set to `1`.

### activeUnderlayOpacity

```ts
activeUnderlayOpacity?: number;
```

Defines the opacity of the underlay when the button is active. By default set to `0`.

### defaultUnderlayOpacity

```ts
defaultUnderlayOpacity?: number;
```

Defines the initial opacity of underlay when the button is inactive. By default set to `0`.

### underlayColor

```ts
underlayColor?: string;
```

Background color of the underlay. This only takes effect when `activeUnderlayOpacity` or `defaultUnderlayOpacity` is set.

### exclusive

```ts
exclusive?: boolean;
```

Defines whether pressing this button prevents other buttons exported by Gesture Handler from being pressed. By default set to `true`.

<HeaderWithBadges platforms={['android']}>
### touchSoundDisabled
</HeaderWithBadges>

```ts
touchSoundDisabled?: boolean;
```

If set to `true`, the system will not play a sound when the button is pressed.

### onPressIn

<CollapsibleCode
label="Show composed types definitions"
expandedLabel="Hide composed types definitions"
lineBounds={[0, 1]}
src={`
onPressIn?: (e: GestureEvent<NativeHandlerData>) => void;

type GestureEvent<NativeHandlerData> = {
handlerTag: number;
numberOfPointers: number;
pointerType: PointerType;
pointerInside: boolean;
}

enum PointerType {
TOUCH,
STYLUS,
MOUSE,
KEY,
OTHER,
}
`}/>

Triggered when the button gets pressed (analogous to `onPressIn` in `Pressable` from RN core).

### onPressOut

<CollapsibleCode
label="Show composed types definitions"
expandedLabel="Hide composed types definitions"
lineBounds={[0, 1]}
src={`
onPressOut?: (e: GestureEvent<NativeHandlerData>) => void;

type GestureEvent<NativeHandlerData> = {
handlerTag: number;
numberOfPointers: number;
pointerType: PointerType;
pointerInside: boolean;
}

enum PointerType {
TOUCH,
STYLUS,
MOUSE,
KEY,
OTHER,
}
`}/>

Triggered when the button gets released or the pointer moves outside of the button area (analogous to `onPressOut` in `Pressable` from RN core).

### onPress

```ts
onPress?: (pointerInside: boolean) => void;
```

Triggered when the button gets pressed (analogous to `onPress` in `Pressable` from RN core).

### onLongPress

```ts
onLongPress?: () => void;
```

Triggered when the button gets pressed for at least [`delayLongPress`](#delaylongpress) milliseconds.


### onActiveStateChange

```ts
onActiveStateChange?: (active: boolean) => void;
```

Triggered when the button transitions between active and inactive states. It passes the current active state as a boolean variable to the method as the first parameter.

### delayLongPress

```ts
delayLongPress?: number;
```

Defines the delay, in milliseconds, after which the [`onLongPress`](#onlongpress) callback gets called. By default set to `600`.


<HeaderWithBadges platforms={['android']}>
### androidRipple
</HeaderWithBadges>

<CollapsibleCode
label="Show composed types definitions"
expandedLabel="Hide composed types definitions"
lineBounds={[0, 1]}
src={`
androidRipple?: PressableAndroidRippleConfig;

type PressableAndroidRippleConfig = {
color?: (string | OpaqueColorValue);
borderless?: boolean;
radius?: number;
foreground?: boolean;
}
`}/>

Configuration for the ripple effect on Android. If not provided, the ripple effect will be disabled. If `{}` is provided, the ripple effect will be enabled with default configuration.
8 changes: 4 additions & 4 deletions packages/docs-gesture-handler/docs/components/touchables.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
id: touchables
title: Touchables
sidebar_label: Touchables
id: legacy-touchables
title: Legacy Touchables
sidebar_label: Legacy Touchables
---

:::warning
Touchables will be removed in the future version of Gesture Handler. Use [`Pressable`](/docs/components/pressable) instead.
Touchables will be removed in the future version of Gesture Handler. Use [`Touchable`](/docs/components/touchable) instead.
:::

Gesture Handler library provides an implementation of RN's touchable components that are based on [native buttons](buttons.mdx) and do not rely on the JS responder system utilized by RN. Our touchable implementation follows the same API and aims to be a drop-in replacement for touchables available in React Native.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ This library is maintained by a very small team. Please keep this in mind when r

- Changing `enabled` prop during a gesture has no effect, only when a gesture starts (that is, a finger touches the screen) the `enabled` prop is taken into consideration to decide whether to extract (or not) the gesture and provide it with stream of events to analyze.
- `Native` gesture may not conform to the standard state flow due to platform specific workarounds to incorporate native views into RNGH.
- Keep in mind that [`Touchables`](/docs/components/touchables) from RNGH are rendering two additional views that may need to be styled separately to achieve desired effect (`style` and `containerStyle` props).
- Keep in mind that [Legacy `Touchables`](/docs/components/legacy-touchables) from RNGH are rendering two additional views that may need to be styled separately to achieve desired effect (`style` and `containerStyle` props).
- In order for the [gesture composition](/docs/fundamentals/gesture-composition) to work, all composed gestures must be attached to the same [`GestureHandlerRootView`](/docs/fundamentals/root-view).

## Multiple instances of Gesture Handler were detected
Expand Down
12 changes: 11 additions & 1 deletion packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,17 @@ code2={

### Buttons

The implementation of buttons has been updated, resolving most button-related issues. They have also been internally rewritten to utilize the new hook API. The original button components are still accessible but have been renamed with the prefix `Legacy`, e.g., `RectButton` is now available as `LegacyRectButton`.
RNGH3 introduces the [`Touchable`](/docs/components/touchable) component — a flexible, unified replacement for all previous button types. While `Touchable` shares the same logic as our standard buttons, it offers a more customizable API.

To help you migrate, here is the current state of our button components:

- [`Touchable`](/docs/components/touchable) - The recommended component. Can be used to replicate both `RectButton` and `BorderlessButton` effects by adjusting the `activeOpacity` and `activeUnderlayOpacity` props.
- To replace `RectButton`, add `activeUnderlayOpacity={0.105}`.
- To replace `BorderlessButton`, add `activeOpacity={0.3}`.

- Standard Buttons (Deprecated) - `BaseButton`, `RectButton` and `BorderlessButton` are still available but are now deprecated. They have been internally rewritten using the new Hooks API to resolve long-standing issues.

- Legacy Buttons (Deprecated): The original, pre-rewrite versions are still accessible, but have been renamed with a `Legacy` prefix (e.g., `LegacyRectButton`).

Although the legacy JS implementation of the buttons is still available, they also use the new host component internally. Because of that, `PureNativeButton` is no longer available in Gesture Handler 3.

Expand Down
Loading
Loading