šŸ“±React Native ā€” The Button šŸ”˜

This is the story of how to create a better basic React Native button

Mikael Ainalem
5 min readApr 19, 2024
Photo by Kentaro Toma on Unsplash

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.

Picture of the initial version with white text
The very first version of the button

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!

The first version that actually looks like a button
The first version that actually looks like a button

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:

  1. The Animated.Value, a variable to hold the animated value.
  2. Handlers to start animations, the triggers that sets the wheels in motion
  3. 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.

A version of the button that animates when pressed
A pressable button

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:

The final result, a button that animates and performs the main action
The final 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!

--

--

Mikael Ainalem

Enthusiastic about software & design, father of 3, freelancer and currently CTO at Norban | twitter: https://twitter.com/mikaelainalem