š±React Native ā The Button š
This is the story of how to create a better basic React Native button
In short: donāt use the React Native ānativeā <Button ā¦ />. It gives a different experience on iOS and Android. What you want to do is to create your own <Button ā¦ /> using a simple but elegant custom component.
In this tutorial, we will go through how to build this component from scratch. Letās go!
Getting started
First up is creating a new clean RN project.
> npx react-native@latest init ReactNativeButton
# ...
> cd ReactNativeButton
And then create the component.
touch Button.tsx
And add the code for the button. Letās start out with something as simple as a View component containing a Text component and some white text. Hereās the code for the component itself.
import React from 'react';
import {StyleSheet, Text, View} from 'react-native';
const Button = () => (
<View>
<Text style={styles.buttonText}>Press me</Text>
</View>
);
const styles = StyleSheet.create({
buttonText: {
color: 'white',
},
});
export default Button;
Now that we have the Button component letās change the default React Native App.tsx to something that renders the <Button />. Hereās what such an App code would look like:
import React from 'react';
import {SafeAreaView, StyleSheet} from 'react-native';
import Button from './Button';
function App(): React.JSX.Element {
return (
<SafeAreaView style={styles.backgroundStyle}>
<Button />
</SafeAreaView>
);
}
const styles = StyleSheet.create({
backgroundStyle: {
backgroundColor: 'black',
flex: 1,
},
});
export default App;
And letās run what we have thus far.
# install pods
npx pod-install
# run it!
yarn && yarn ios
Executing the above commands gives us the following rendering.
Adding some styling
Well, not so impressive. Letās quickly add some styling to make it a bit more what we would expect it to look like in its shipped state.
const styles = StyleSheet.create({
buttonContainer: {
alignItems: 'center',
backgroundColor: '#EAB68F',
borderRadius: 5,
marginHorizontal: 10,
padding: 15,
},
buttonText: {
color: 'black',
fontSize: 20,
},
});
And the result. Better!
Making the component clickable
As the next step we want to make the component clickable. Changing the View to a Pressable and adding handlers gives us the fundamentals we need. Note: we donāt want to use the onPress handler as we want to animate the background to and from its active state. Using the onPressIn and onPressOut gives us the possibility to add an animation on the press and release evens. Hereās what the modified button looks like.
const Button = () => {
const handlePress = () => {
console.log('Pressed');
};
const handleRelease = () => {
console.log('Released');
};
return (
<Pressable
style={styles.buttonContainer}
onPressIn={handlePress}
onPressOut={handleRelease}>
<Text style={styles.buttonText}>Press me</Text>
</Pressable>
);
};
// ...
Looking at the Metro output, when running the app again, shows the presses in and out when clicking the button.
# console output
LOG Pressed
LOG Released
Animating the button
Now, letās animate the background color, the backgroundColor style property. The animation consists of 3 major parts. They are:
- The Animated.Value, a variable to hold the animated value.
- Handlers to start animations, the triggers that sets the wheels in motion
- Interpolation to get the right color values, converting the animated value to a corresponding color value
Note: To animate components, one needs to use the Animated counterpart component. In this case, the Animated.View over the ordinary View. This is what whole thing looks like in code:
const Button = () => {
// 1. Animated value
const backgroundColorRef = new Animated.Value(0);
// 2. The handlers
const handlePress = () => {
Animated.timing(backgroundColorRef, {
toValue: 1,
duration: 60,
useNativeDriver: true,
}).start();
};
const handleRelease = () => {
Animated.timing(backgroundColorRef, {
toValue: 0,
duration: 60,
useNativeDriver: true,
}).start();
};
// Interpolate the background color
const backgroundColor = backgroundColorRef.interpolate({
inputRange: [0, 1],
outputRange: ['#EAB68F', '#D98E73'],
});
// Applying the interpolated backgroundColor
return (
<Pressable onPressIn={handlePress} onPressOut={handleRelease}>
<Animated.View style={[styles.buttonContainer, {backgroundColor}]}>
<Text style={styles.buttonText}>Press me</Text>
</Animated.View>
</Pressable>
);
}
One important thing to keep in mind here is having a sort animation duration. Here itās set to 60ms. Clicking a button should be snappy and smooth.
Letās have a look at what we have thus far and what it looks like on iOS and Android.
The action
Now for the main thing: the action. Letās add a handler for the action. Hereās where the standard handler onPress comes in play.
// ...
return (
<Pressable
onPressIn={handlePress}
onPressOut={handleRelease}
onPress={() => Alert.alert('Action')}>
>
{ /* ... */ }
</Pressable>
);
// ...
and the result:
Accessibility
Last, but absolutely not least, we need to make the button accessible to all users. To do so, letās ask ChatGPT. Hey ChatGPT, I have this React Native Button component: ā¦ What modifications do I need for making it accessible according to the React Native accessibility features?
// ...
return (
<Pressable
// ...
accessible={true}
accessibilityRole="button"
accessibilityLabel="Press me"
>
{ /* ... */ }
</Pressable>
);
// ...
Thatās it! Thanks for reading and good luck with your buttons and React Native projects.
If you enjoy articles like this one, be sure to clap, share, and support my work. Cheers!