Pressable is a modern React Native component for handling touches, replacing the legacy TouchableHighlight, TouchableOpacity, and TouchableWithoutFeedback. Pressable provides a unified API with detection of pressed, hovered, and focused states. The component was introduced in React Native 0.63 and is recommended by Meta for all new projects. Learn more in Meta’s documentation.
Key Takeaways
Pressable is a React Native component that provides a unified interface for handling all types of touches. Unlike three different Touchable components (TouchableHighlight, TouchableOpacity, TouchableWithoutFeedback), Pressable replaces them all with one flexible state and styling system.
The component was introduced in React Native 0.63 (September 2020). According to Meta (2026), 70% of new React Native projects use Pressable instead of Touchable components. Pressable solves the key problem of Touchable — a fragmented API and the need to switch between components for different effects.
Unlike TouchableOpacity, where the press effect is hardcoded (opacity change), Pressable provides a function that receives the current state. The developer decides how to visually respond to a press: change color, scale, shadow, or a combination of effects. This gives full control over UX.
Pressable defines three interaction states: pressed (finger on screen), hovered (cursor over element on Web/TV), focused (element in keyboard focus). Each state triggers the corresponding callback and is passed to the style function and the child render function.
| State | Condition | Typical reaction |
|---|---|---|
| pressed | Finger on screen or click held | Opacity, scale, or background color change |
| hovered | Cursor over element (Web, TV) | Highlight, shadow, underline |
| focused | Element in keyboard focus | Outline, border, color change |
Important: hovered and focused only work on platforms with pointer support (Web, Android TV, Apple TV). On mobile devices, only pressed is available. For cross-platform applications, design visual reactions considering this limitation.
<Pressable
style={({ pressed }) => [
styles.button,
pressed && styles.buttonPressed,
]}
onPress={handlePress}>
{({ pressed }) => (
<Text style={pressed ? styles.textPressed : styles.text}>
{pressed ? 'Release' : 'Press'}
</Text>
)}
</Pressable>Pressable API includes four main callbacks: onPress (full press with release), onLongPress (long press), onPressIn (touch start), and onPressOut (touch end). The combination of onPressIn and onPressOut allows creating custom animations.
Additional props: delayLongPress (long press delay, default 500 ms), pressRetentionOffset (area beyond which the press is cancelled), android_ripple (ripple effect on Android). Note: Ripple only works on Android 5+.
<Pressable
onPress={() => console.log('Pressed')}
onLongPress={() => console.log('Long press')}
onPressIn={() => console.log('Finger down')}
onPressOut={() => console.log('Finger up')}
delayLongPress={300}
android_ripple={{ color: 'rgba(0,0,0,0.1)', borderless: false }}
style={styles.button}>
<Text>Press me</Text>
</Pressable>onPress is only called if the finger is lifted within the component area (accounting for pressRetentionOffset). If the user moves their finger outside the bounds, onPress is not triggered, preventing false presses. onPressOut is called in any case upon release.
hitSlop is a Pressable prop that extends the touch area beyond the visible component bounds. This is critical for small elements: icons, checkboxes, radio buttons. Apple HIG recommends a minimum touch target size of 44×44 points. hitSlop allows meeting this requirement without changing the visual size.
hitSlop accepts an object { top, bottom, left, right } or a single number for all sides. Values are specified in logical pixels (dp/pt). For 24×24 icons, a hitSlop of at least 10–12 on each side is recommended to achieve the 44×44 touch target.
<Pressable
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
onPress={handleIconPress}
style={styles.iconButton}>
<Icon name="star" size={24} />
</Pressable>pressRetentionOffset — the inverse of hitSlop: defines the area the user can move outside the component bounds without cancelling the press. Defaults to hitSlop. If you need the press to persist when moving outside the bounds, increase pressRetentionOffset.
Pressable vs Touchable — a key question when choosing a component for handling touches. TouchableOpacity, TouchableHighlight, and TouchableWithoutFeedback are legacy components that will not be removed but will not receive new features. Meta recommends Pressable for new projects.
Key advantages of Pressable: a unified API for all effect types, access to the pressed state for custom styling, hovered/focused state support, hitSlop and pressRetentionOffset, onLongPress callback without additional configuration. TouchableOpacity is only suitable for simple opacity changes.
| Scenario | Touchable | Pressable |
|---|---|---|
| Simple press | TouchableOpacity | Pressable + opacity in style |
| Background highlight | TouchableHighlight | Pressable + backgroundColor |
| No visual effect | TouchableWithoutFeedback | Pressable with empty style |
| Long press | onLongPress on any Touchable | onLongPress on Pressable |
| Custom animation | Animated + onPressIn/Out | Pressable + useAnimatedStyle |
| Ripple on Android | TouchableNativeFeedback | Pressable + android_ripple |
Pressable allows creating complex interactive elements with minimal code. Below is an example of an animated button with scale change on press and ripple effect on Android. Such a button replaces the TouchableOpacity + Animated combination.
const AnimatedButton = ({ title, onPress }) => {
const scale = React.useRef(new Animated.Value(1)).current;
const handlePressIn = () => {
Animated.spring(scale, {
toValue: 0.96,
useNativeDriver: true,
}).start();
};
const handlePressOut = () => {
Animated.spring(scale, {
toValue: 1,
friction: 3,
useNativeDriver: true,
}).start();
};
return (
<Pressable
onPress={onPress}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
android_ripple={{ color: 'rgba(255,255,255,0.3)' }}
style={styles.button}>
<AnimatedView style={[styles.inner, { transform: [{ scale }] }]}>
<Text style={styles.text}>{title}</Text>
</AnimatedView>
</Pressable>
);
};Important: use useNativeDriver: true for animations in Pressable. This offloads the animation to the native thread, preventing frame drops during fast presses. Native driver is supported for transform and opacity on all platforms.
Frequently Asked Questions
No, Pressable has a different API. TouchableOpacity automatically applies opacity on press. Pressable requires explicit style specification via a state function. The replacement is trivial: pass style={({ pressed }) => [{ opacity: pressed ? 0.5 : 1 }]}.
Use the android_ripple prop with an object { color, borderless }. The ripple color is specified in rgba format. For round buttons, use borderless: true. Ripple only works on Android 5+. On iOS, use custom animation.
Pressable has built-in protection against accidental presses during scrolling. If the user scrolls a list, onPress is not called. This behavior can be configured via pressRetentionOffset and delayPressIn.
Use the onLongPress prop — built-in long press support. The delay is configurable via delayLongPress (default 500 ms). For context menus and drag-and-drop, combine onLongPress with onPressIn.
Yes, Pressable supports the focused state for keyboard navigation on Android TV, Apple TV, and Web. Use styling via { focused } to display focus. On mobile devices, the focused state is not applicable.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also