apps/docs/content/docs/en/native/components/(utilities)/pressable-feedback.mdx
<NativeVideoPlayerView target="auto" srcLight="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/native/components/videos/pressable-feedback-docs-light.mp4" srcDark="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/native/components/videos/pressable-feedback-docs-dark.mp4" />
import { PressableFeedback } from 'heroui-native';
<PressableFeedback>
<PressableFeedback.Highlight />
<PressableFeedback.Ripple />
<PressableFeedback.Scale>...</PressableFeedback.Scale>
</PressableFeedback>
animation={false} to disable the built-in scale when using PressableFeedback.Scale instead.className / style to the scale wrapper.PressableFeedback provides press-down scale feedback out of the box. This is the recommended way to use it in most cases.
<PressableFeedback>...</PressableFeedback>
Add a highlight overlay for iOS-style feedback effect alongside the built-in scale.
<PressableFeedback>
<PressableFeedback.Highlight />
...
</PressableFeedback>
Add a ripple overlay for Android-style feedback effect alongside the built-in scale.
<PressableFeedback>
<PressableFeedback.Ripple />
...
</PressableFeedback>
Customize the built-in scale animation via the animation.scale prop. Accepts value, timingConfig, and ignoreScaleCoefficient.
<PressableFeedback
animation={{
scale: {
value: 0.9,
timingConfig: { duration: 150 },
ignoreScaleCoefficient: true,
},
}}
>
...
</PressableFeedback>
Configure highlight overlay opacity and background color.
<PressableFeedback>
<PressableFeedback.Highlight
animation={{
backgroundColor: { value: '#3b82f6' },
opacity: { value: [0, 0.2] },
}}
/>
...
</PressableFeedback>
Configure ripple effect color, opacity, and duration.
<PressableFeedback>
<PressableFeedback.Ripple
animation={{
backgroundColor: { value: '#ec4899' },
opacity: { value: [0, 0.1, 0] },
progress: { baseDuration: 600 },
}}
/>
...
</PressableFeedback>
When you need to apply the scale animation to a specific element inside the container rather than the root itself, disable the root's built-in scale with animation={false} and use the PressableFeedback.Scale compound part. This gives you full control over which element scales and lets you apply className / style directly to the scale wrapper.
<PressableFeedback animation={false}>
<PressableFeedback.Scale>...</PressableFeedback.Scale>
</PressableFeedback>
You can combine it with Highlight or Ripple inside the Scale wrapper:
<PressableFeedback animation={false}>
<PressableFeedback.Scale>
<PressableFeedback.Highlight />
...
</PressableFeedback.Scale>
</PressableFeedback>
Set animation="disable-all" on the root to cascade-disable all animations including the built-in scale and any child compound parts (Scale, Highlight, Ripple).
<PressableFeedback animation="disable-all">...</PressableFeedback>
You can also disable all animations while keeping a scale config (e.g. for toggling at runtime):
<PressableFeedback animation={{ scale: { value: 0.97 }, state: 'disable-all' }}>
...
</PressableFeedback>
import { PressableFeedback, Card, Button } from 'heroui-native';
import { Image } from 'expo-image';
import { LinearGradient } from 'expo-linear-gradient';
import { StyleSheet, View, Text } from 'react-native';
export default function PressableFeedbackExample() {
return (
<PressableFeedback className="w-full aspect-square overflow-auto">
<Card className="flex-1">
<Image
source={{
uri: 'https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/neo2.jpeg',
}}
style={StyleSheet.absoluteFill}
contentFit="cover"
/>
<LinearGradient
colors={['rgba(0,0,0,0.1)', 'rgba(0,0,0,0.4)']}
style={StyleSheet.absoluteFill}
/>
<PressableFeedback.Ripple
animation={{
backgroundColor: { value: 'white' },
opacity: { value: [0, 0.3, 0] },
}}
/>
<View className="flex-1 gap-4" pointerEvents="box-none">
<Card.Body className="flex-1" pointerEvents="none">
<Card.Title className="text-base text-zinc-50 uppercase mb-0.5">
Neo
</Card.Title>
<Card.Description className="text-zinc-50 font-medium text-base">
Home robot
</Card.Description>
</Card.Body>
<Card.Footer className="gap-3">
<View className="flex-row items-center justify-between">
<View pointerEvents="none">
<Text className="text-base text-white">Available soon</Text>
<Text className="text-base text-zinc-300">Get notified</Text>
</View>
<Button size="sm" className="bg-white">
<Button.Label className="text-black">Notify me</Button.Label>
</Button>
</View>
</Card.Footer>
</View>
</Card>
</PressableFeedback>
);
}
You can find more examples in the GitHub repository.
| prop | type | default | description |
|---|---|---|---|
children | React.ReactNode | - | Content to be wrapped with press feedback |
isDisabled | boolean | false | Whether the pressable component is disabled |
className | string | - | Additional CSS classes |
animation | PressableFeedbackRootAnimation | - | Customize scale via { scale: ... }, false to disable root scale, 'disable-all' to cascade-disable all |
isAnimatedStyleActive | boolean | true | Whether the root's built-in animated styles are active |
asChild | boolean | false | Whether to render as a child element |
...rest | AnimatedProps<PressableProps> | - | All Reanimated Animated Pressable props are supported |
The root animation prop supports the standard AnimationRoot control flow:
true or undefined: Use the default built-in scale animationfalse or "disabled": Disable the root's built-in scale (use this when applying scale via PressableFeedback.Scale instead)"disable-all": Cascade-disable all animations including the built-in scale and children (Scale, Highlight, Ripple)object: Custom configuration for the built-in scale| prop | type | default | description |
|---|---|---|---|
scale | PressableFeedbackScaleAnimation | - | Customize the built-in scale animation (value, timingConfig, etc.) |
state | 'disabled' | 'disable-all' | boolean | - | Control animation state while keeping configuration (e.g. for runtime toggling) |
Use this compound part when you need to apply scale to a specific child element inside the container, instead of scaling the root itself. Set animation={false} on the root to disable its built-in scale when using this component.
| prop | type | default | description |
|---|---|---|---|
className | string | - | Additional CSS classes |
animation | PressableFeedbackScaleAnimation | - | Animation configuration for scale effect |
isAnimatedStyleActive | boolean | true | Whether animated styles (react-native-reanimated) are active |
style | ViewStyle | - | Additional styles |
...AnimatedProps | AnimatedProps<ViewProps> | - | All Reanimated Animated View props are supported |
Animation configuration for scale effect. Can be:
false or "disabled": Disable scale animationtrue or undefined: Use default scale animationobject: Custom scale configuration| prop | type | default | description |
|---|---|---|---|
state | 'disabled' | boolean | - | Disable animations while customizing properties |
value | number | 0.985 | Scale value when pressed (automatically adjusted based on container width) |
timingConfig | WithTimingConfig | { duration: 300, easing: Easing.out(Easing.ease) } | Animation timing configuration |
ignoreScaleCoefficient | boolean | false | Ignore automatic scale coefficient and use the scale value directly |
| prop | type | default | description |
|---|---|---|---|
className | string | - | Additional CSS classes |
animation | PressableFeedbackHighlightAnimation | - | Animation configuration for highlight overlay |
isAnimatedStyleActive | boolean | true | Whether animated styles (react-native-reanimated) are active |
style | ViewStyle | - | Additional styles |
...AnimatedProps | AnimatedProps<ViewProps> | - | All Reanimated Animated View props are supported |
Animation configuration for highlight overlay. Can be:
false or "disabled": Disable highlight animationstrue or undefined: Use default animationsobject: Custom animation configuration| prop | type | default | description |
|---|---|---|---|
state | 'disabled' | boolean | - | Disable animations while customizing properties |
opacity.value | [number, number] | [0, 0.1] | Opacity values [unpressed, pressed] |
opacity.timingConfig | WithTimingConfig | { duration: 200 } | Animation timing configuration |
backgroundColor.value | string | Theme-aware gray | Background color of highlight overlay |
| prop | type | default | description |
|---|---|---|---|
className | string | - | Additional CSS classes for container slot |
classNames | ElementSlots<RippleSlots> | - | Additional CSS classes for slots (container, ripple) |
styles | Partial<Record<RippleSlots, ViewStyle>> | - | Styles for different parts of the ripple overlay |
animation | PressableFeedbackRippleAnimation | - | Animation configuration for ripple overlay |
isAnimatedStyleActive | boolean | true | Whether animated styles (react-native-reanimated) are active |
...ViewProps | Omit<ViewProps, 'style'> | - | All View props except style are supported |
styles| prop | type | description |
|---|---|---|
container | ViewStyle | Styles for the container slot |
ripple | ViewStyle | Styles for the ripple slot |
Animation configuration for ripple overlay. Can be:
false or "disabled": Disable ripple animationstrue or undefined: Use default animationsobject: Custom animation configuration| prop | type | default | description |
|---|---|---|---|
state | 'disabled' | boolean | - | Disable animations while customizing properties |
backgroundColor.value | string | Computed based on theme | Background color of ripple effect |
progress.baseDuration | number | 1000 | Base duration for ripple progress (automatically adjusted based on diagonal) |
progress.minBaseDuration | number | 750 | Minimum base duration for the ripple progress animation |
progress.ignoreDurationCoefficient | boolean | false | Ignore automatic duration coefficient and use base duration directly |
opacity.value | [number, number, number] | [0, 0.1, 0] | Opacity values [start, peak, end] for ripple animation |
opacity.timingConfig | WithTimingConfig | { duration: 200 } | Animation timing configuration |
scale.value | [number, number, number] | [0, 1, 1] | Scale values [start, peak, end] for ripple animation |
scale.timingConfig | WithTimingConfig | { duration: 200 } | Animation timing configuration |
ElementSlots<RippleSlots>Additional CSS classes for ripple slots:
| slot | description |
|---|---|
container | Outer container slot (absolute inset-0) - styles can be fully customized |
ripple | Inner ripple slot (absolute top-0 left-0 rounded-full) - has animated properties that cannot be set via className |