Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 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 [`Clickable`](/docs/components/clickable) instead.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, maybe we would like to keep RawButton? This would keep more flexibility for advanced users. But that's just a thought, not strong opinion - I'm not sure if we want to do that (cc @j-piasecki, @akwasniewski)

:::

<GifGallery>
<img src={useBaseUrl('gifs/samplebutton.gif')} width="280" />
</GifGallery>
Expand Down
275 changes: 275 additions & 0 deletions packages/docs-gesture-handler/docs/components/clickable.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
---
id: clickable
title: Clickable
sidebar_label: Clickable
---

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

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

With `Clickable`, you have more control over animations. It is possible to choose between animating the entire component or just the underlay, along with specifying desired `opacity` values. This allows you to effortlessly replicate both `RectButton` and `BorderlessButton` effects using a single unified component. Furthermore, it resolves many legacy issues found in earlier versions. On Android, you can opt for the native ripple effect or leverage JS-based animations via the [Animated API](https://reactnative.dev/docs/animated).

## Replacing old buttons

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

### RectButton

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

```tsx
<Clickable
...
underlayActiveOpacity={0.105}/>
```

### BorderlessButton

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

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

## Example

In this example we will demonstrate how to recreate `RectButton` and `BorderlessButton` effects using `Clickable` 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,
Clickable,
} from 'react-native-gesture-handler';

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

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

<Clickable
onPress={() => {
console.log('BorderlessButton built with Clickable');
}}
style={[styles.button, { backgroundColor: '#5f97c8' }]}
activeOpacity={0.3}>
<Text style={styles.buttonText}>BorderlessButton</Text>
</Clickable>
</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

### exclusive

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

Defines if more than one button could be pressed simultaneously. 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 `TouchableHighlight` 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 (analogous to `onPressOut` in `TouchableHighlight` from RN core).

### onPress

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

Triggered when the button gets pressed (analogous to `onPress` in `TouchableHighlight` 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`.

### underlayColor

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

Background color of underlay. This only takes effect when `underlayActiveOpacity` is set.

### underlayActiveOpacity

```ts
underlayActiveOpacity?: number;
```

Defines the opacity of underlay when the button is active. If not set, underlay won't be rendered.

### activeOpacity

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

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

### underlayInitialOpacity

```ts
underlayInitialOpacity?: number;
```

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

### initialOpacity

```ts
initialOpacity?: number;
```

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

<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.
10 changes: 9 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,15 @@ 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 [`Clickable`](/docs/components/clickable) component — a flexible, unified replacement for all previous button types. While `Clickable` 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:

- [`Clickable`](/docs/components/clickable) - The recommended component.

- 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
2 changes: 2 additions & 0 deletions skills/gesture-handler-3-migration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ The implementation of buttons has been updated, resolving most button-related is

`PureNativeButton` has been removed. If encountered, inform the user that it has been removed and let them decide how to handle that case. They can achieve similar functionality with other buttons.

When migrating buttons, you should use `Clickable` component instead. To replace `BaseButton` use `Clickable` with default props, to replace `RectButton` use `Clickable` with `underlayActiveOpacity={0.105}` and to replace `BorderlessButton` use `Clickable` with `activeOpacity={0.3}`.

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`.

Rename all instances of createNativeWrapper to legacy_createNativeWrapper. This includes both the import statements and the function calls.
Expand Down
Loading