If you've developed React Native applications before, you certainly used the
<Button />
component at some point and realized how
limited it is, since the <Button />
component uses the
native style and doesn't allow for much customization.
You might have turned to the <TouchableOpacity />
or
<TouchableHighlight />
components to create buttons
that look more like the ones you see in different apps. These components are
more flexible and allow you to customize the look and feel of your buttons
to match your app's design, they also provide a better user experience by
giving users a visual feedback when they press the button.
But then when React Native v0.63 was released, the <Pressable />
component was introduced, and it's the recommended way to handle user interactions
within your app. Although it improved the API for handling touch events, it
doesn't provide all the built-in styles and effects that the Touchables do.
You can read more about why the <Pressable />
component was introduced
on the React Native v0.63 announcement blog post.
Setup project
I'll be using Expo and React Native Reanimated to create the examples in this post, but you can translate the code to use the React Native Animated API if you prefer.
To set up this project, I ran the following command:
bun create expo pressables --template blank-typescript
cd pressables
bun expo install react-native-reanimated
bun ios
Pressable with opacity
Let's start by creating the same visual feedback that the <TouchableOpacity />
component provides, which is the opacity effect when the user presses the button.
import {
Pressable,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
} from "react-native-reanimated";
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
export default function App() {
const pressed = useSharedValue(false);
const animatedStyle = useAnimatedStyle(() => {
const opacity = pressed.value
? 0.2
: withTiming(1, {
duration: 200,
});
return {
opacity: opacity,
backgroundColor: "purple",
padding: 20,
borderRadius: 5,
width: 150,
justifyContent: "center",
alignItems: "center",
};
});
return (
<View style={styles.container}>
<TouchableOpacity
style={{
backgroundColor: "purple",
padding: 20,
borderRadius: 5,
width: 150,
justifyContent: "center",
alignItems: "center",
}}
>
<Text style={styles.buttonText}>Touchable</Text>
</TouchableOpacity>
<AnimatedPressable
style={animatedStyle}
onPressIn={() => {
pressed.value = true;
}}
onPressOut={() => {
pressed.value = false;
}}
>
<Text style={styles.buttonText}>Pressable</Text>
</AnimatedPressable>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
gap: 10,
justifyContent: "center",
},
buttonText: {
fontSize: 20,
},
});
With the code above we created a custom <Pressable />
component
that has the same opacity effect as the <TouchableOpacity />
component,
check it out:
Pressable with scale
This is one of my favorites effects, it gives a nice feedback to the user when they press the button, and it's very easy to implement. Let's cut to the chase and see the code:
import {
Pressable,
StyleSheet,
Text,
View,
} from "react-native";
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
} from "react-native-reanimated";
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
export default function App() {
const pressed = useSharedValue(false);
const animatedStyle = useAnimatedStyle(() => {
const scale = withTiming(pressed.value ? 0.92 : 1, {
duration: 100,
});
return {
transform: [{ scale }],
backgroundColor: "darkblue",
padding: 20,
borderRadius: 5,
width: 150,
justifyContent: "center",
alignItems: "center",
};
});
return (
<View style={styles.container}>
<AnimatedPressable
style={animatedStyle}
onPressIn={() => {
pressed.value = true;
}}
onPressOut={() => {
pressed.value = false;
}}
>
<Text style={styles.buttonText}>Pressable</Text>
</AnimatedPressable>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
gap: 10,
justifyContent: "center",
},
buttonText: {
fontSize: 20,
color: 'white',
fontWeight: 'bold',
},
});
And now the result:
Pressable with haptic feedback
This is a very underused feature in React Native, but it's very easy to implement, specially with the help of the Expo Haptics library.
Apple has a great article on best practices for haptic feedback in iOS apps, and I highly recommend you check it out: Playing Haptics.
Unfortunately, I can't show you the haptic feedback on video, but you can try it out on your own device. So let's see the code, but first, make sure to install the Expo Haptics library:
bun expo install expo-haptics
And now the code:
import {
Pressable,
StyleSheet,
Text,
View,
} from "react-native";
export default function App() {
return (
<View style={styles.container}>
<Pressable
style={styles.button}
onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)}
>
<Text style={styles.buttonText}>Light</Text>
</Pressable>
<Pressable
style={styles.button}
onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium)}
>
<Text style={styles.buttonText}>Medium</Text>
</Pressable>
<Pressable
style={styles.button}
onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy)}
>
<Text style={styles.buttonText}>Heavy</Text>
</Pressable>
<Pressable
style={styles.button}
onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Rigid)}
>
<Text style={styles.buttonText}>Rigid</Text>
</Pressable>
<Pressable
style={styles.button}
onPress={() => Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Soft)}
>
<Text style={styles.buttonText}>Soft</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
gap: 10,
justifyContent: "center",
},
button: {
backgroundColor: "lightblue",
padding: 10,
borderRadius: 5,
width: 150,
alignItems: "center",
},
buttonText: {
fontSize: 20,
color: "darkblue",
fontWeight: "bold",
},
});
I really like using haptic feedback on iOS, it gives a nice touch to the app, and if you combine with long press, it can be a great way to show the user that they can do something else with the button, rewarding them with a nice feeling.
Conclusion
This is some ways to supercharge your <Pressable />
components
in React Native, and I hope you found this post helpful.
If you have any questions or suggestions, feel free to reach out to me, you can find my socials at the home page of my blog.