Editor’s note: This post was updated 14 January 2022 to improve the tutorials and include a “modern” button styling example.
React Native is an excellent framework for building native mobile applications. It allows you to build apps that will work on both iOS and Android platforms, but core UI components such as <Button />
will look different on each platform. This is because React Native renders platform-specific UI elements, and as a result, there are limited styling and customization options (the official React Native docs admit as much).
For this and many other reasons, it’s critical to know how to create buttons that look consistent regardless of the operating system. In this guide, we’ll walk you through how to create various types of custom buttons in React Native, including:
<TouchableOpacity />
component to build buttons with different background colors, sizes, border styles, and text stylesRun the following command to get started.
npx react-native init CustomButtonDemo
Remove all the boilerplate code from the App.js
file and add the following.
import React from "react"; import { View, Button, StyleSheet } from "react-native"; const App = () => { return ( <View style={styles.screenContainer}> <Button title="Hey there!" /> </View> ); }; const styles = StyleSheet.create({ screenContainer: { flex: 1, justifyContent: "center", padding: 16 } }); export default App;
In the above code block, a core <Button />
component is declared and wrapped inside a container. StyleSheet
is an API provided by the React Native framework as an abstraction to CSS stylesheets.
<TouchableOpacity />
to create custom button componentsNow that you’ve set up the main screen, it’s time to turn your attention to the custom button component.
const AppButton = props => ( // ... )
Name the custom button component AppButton
.
Import the <TouchableOpacity />
and <Text />
components from react-native
.
import { View, Button, StyleSheet, TouchableOpacity, Text } from "react-native";
To create custom buttons, you need to customize the <TouchableOpacity />
component and include the <Text />
component inside of it to display the button text.
const AppButton = ({ onPress, title }) => ( <TouchableOpacity onPress={onPress} style={styles.appButtonContainer}> <Text style={styles.appButtonText}>{title}</Text> </TouchableOpacity> );
Next, create the StyleSheet
properties to style the button.
const styles = StyleSheet.create({ // ... appButtonContainer: { elevation: 8, backgroundColor: "#009688", borderRadius: 10, paddingVertical: 10, paddingHorizontal: 12 }, appButtonText: { fontSize: 18, color: "#fff", fontWeight: "bold", alignSelf: "center", textTransform: "uppercase" } });
The custom button should now look like this:
<TouchableOpacity />
, as the name suggests, is a touchable component, which means it can respond to the user’s touch. When you press the button, the opacity decreases. You can control the opacity by passing an activeOpacity
prop to the <TouchableOpacity />
component.
const AppButton = ({ onPress, title }) => ( <TouchableOpacity activeOpacity={0.8} onPress={onPress} style={styles.appButtonContainer} > <Text style={styles.appButtonText}>{title}</Text> </TouchableOpacity> );
If you want to change the opacity for all of the custom buttons in your app, use the defaultProps
property. defaultProps
is a React component property that sets default values for the prop argument.
After you’ve imported TouchableOpacity
, add the following line at the top of the file.
TouchableOpacity.defaultProps = { activeOpacity: 0.8 };
The onPress
prop expects a function or a function reference that will execute when the user presses the button.
The full code for this section is as follows:
import React from "react"; import { View, Button, StyleSheet, TouchableOpacity, Text } from "react-native"; TouchableOpacity.defaultProps = { activeOpacity: 0.8 }; const AppButton = ({ onPress, title }) => ( <TouchableOpacity onPress={onPress} style={styles.appButtonContainer}> <Text style={styles.appButtonText}>{title}</Text> </TouchableOpacity> ); const App = () => { return ( <View style={styles.screenContainer}> <AppButton title="Hey there!" size="sm" backgroundColor="#007bff" /> </View> ); }; const styles = StyleSheet.create({ screenContainer: { flex: 1, justifyContent: "center", padding: 16 }, appButtonContainer: { elevation: 8, backgroundColor: "#009688", borderRadius: 10, paddingVertical: 10, paddingHorizontal: 12 }, appButtonText: { fontSize: 18, color: "#fff", fontWeight: "bold", alignSelf: "center", textTransform: "uppercase" } }); export default App;
Since it’s a custom button component, you have the liberty of piling on additional props. For example, you can add a prop to change the button size or change the background color.
const AppButton = ({ onPress, title, size, backgroundColor }) => ( <TouchableOpacity onPress={onPress} style={[ styles.appButtonContainer, size === "sm" && { paddingHorizontal: 8, paddingVertical: 6, elevation: 6 }, backgroundColor && { backgroundColor } ]} > <Text style={[styles.appButtonText, size === "sm" && { fontSize: 14 }]}> {title} </Text> </TouchableOpacity> ); // ... <AppButton title="Hey there!" size="sm" backgroundColor="#007bff" />;
You’ll see the following result on your screen.
By default, React Native doesn’t have an API to create a linear gradient background in a container. Luckily, there’s another utility library for React Native that you can use to create linear gradient colors. It is very flexible and offers various props to customize your pre-built component’s gradient style according to your needs. You can learn more about the linear gradient library on GitHub.
Run the following command to include the library in your project.
npm i react-native-linear-gradient
Next, import the <LinearGradient />
component from the react-native-linear-gradient
library.
import LinearGradient from "react-native-linear-gradient";
You‘ll need to make some adjustments in the <AppButton />
component. Wrap the <TouchableOpacity />
component around the <LinearGradient />
component and add the style prop to the <LinearGradient />
component.
const AppButton = ({ onPress, title }) => ( <TouchableOpacity onPress={onPress}> <LinearGradient colors={["#004d40", "#009688"]} style={styles.appButtonContainer} > <Text style={styles.appButtonText}>{title}</Text> </LinearGradient> </TouchableOpacity> );
The colors
prop in LinearGradient
accepts an array, which contains the color values that will be used to create the linear gradient.
Check out the complete code for this section below.
import React from "react"; import { View, Button, StyleSheet, TouchableOpacity, Text } from "react-native"; import LinearGradient from "react-native-linear-gradient"; TouchableOpacity.defaultProps = { activeOpacity: 0.8 }; const AppButton = ({ onPress, title }) => ( <TouchableOpacity onPress={onPress}> <LinearGradient colors={["#004d40", "#009688"]} style={styles.appButtonContainer} > <Text style={styles.appButtonText}>{title}</Text> </LinearGradient> </TouchableOpacity> ); const App = () => { return ( <View style={styles.screenContainer}> <AppButton title="Hey there!" size="sm" backgroundColor="#007bff" /> </View> ); }; const styles = StyleSheet.create({ screenContainer: { flex: 1, justifyContent: "center", padding: 16 }, appButtonContainer: { elevation: 8, borderRadius: 10, paddingVertical: 10, paddingHorizontal: 12 }, appButtonText: { fontSize: 18, color: "#fff", fontWeight: "bold", alignSelf: "center", textTransform: "uppercase" } }); export default App;
Let’s see another example. The following <AppButton/>
definition creates a gradient-style-based custom button with a blue-color gradient and a neon green light line.
const AppButton = ({ onPress, title }) => ( <TouchableOpacity onPress={onPress}> <LinearGradient colors={["#1387d4", "#259399", "#0b466e"]} start={{x: 0, y: 0}} // Gradient starting coordinates end={{x: 0, y: 0.5}} // Gradient ending coordinates style={styles.appButtonContainer} > <Text style={styles.appButtonText}>{title}</Text> </LinearGradient> </TouchableOpacity> );
Here we can use the start
and end
props to control the gradient position. The above component definition produces the following button.
Similarly, you can build your own gradient button style matching your styling requirements in design prototypes.
React Native developers often have to create buttons with icons according to the design prototypes they receive. These icon buttons are widely used in login screens, dashboards, and various mobile application screens. Now, we’re going to create some buttons by using several vector icons.
There are two key approaches for creating these special buttons:
<Text/>
element and <Image/>
element with <View/>
You can easily create these types of buttons with the react-native-vector-icons
library.
Let’s create several different buttons for a sign-in page. Here’s what our sign-in page will look like when we’re finished.
First, add the vector icons library with the following command.
npm i react-native-vector-icons
Next, import the <Icon />
component from the library, as shown below.
import Icon from 'react-native-vector-icons/FontAwesome';
Now we can create a reusable icon button with the following definition.
const AppButton = ({ onPress, icon, title, backgroundColor }) => ( <View style={styles.appButtonContainer}> <Icon.Button name={icon} backgroundColor={backgroundColor} onPress={onPress} style={styles.appButton} > <Text style={styles.appButtonText}>{title}</Text> </Icon.Button> </View> );
The icon button component is ready. You can configure it by sending different props: onPress
, icon
, title
, and backgroundColor
. Check out the source code for the sample sign-in page below.
import React from "react"; import { View, StyleSheet, Text } from "react-native"; import Icon from 'react-native-vector-icons/FontAwesome'; const AppButton = ({ onPress, icon, title, backgroundColor }) => ( <View style={styles.appButtonContainer}> <Icon.Button name={icon} backgroundColor={backgroundColor} onPress={onPress} style={styles.appButton} > <Text style={styles.appButtonText}>{title}</Text> </Icon.Button> </View> ); const App = () => { return ( <View style={styles.screenContainer}> <AppButton icon="sign-in" title="Login with password" backgroundColor="#777"/> <AppButton icon="facebook" title="Login with Facebook" backgroundColor="#3b5998"/> <AppButton icon="github" title="Login with GitHub" backgroundColor="#14191e"/> </View> ); }; const styles = StyleSheet.create({ screenContainer: { flex: 1, justifyContent: "center", padding: 80, backgroundColor: "#555", }, appButton: { padding: 12, }, appButtonText: { fontSize: 17, }, appButtonContainer: { paddingVertical: 10, paddingHorizontal: 12, }, }); export default App;
The above source code configures the same <AppButton/>
component definition by sending different props.
As in any React application, you can add styles to your component based on the current value of the state. For example, if you want to disable a button for a certain period of time after it’s pressed, the button must have a disabled background color so that the user knows it’s temporarily inactive.
Add a style property inside the StyleSheet
object to represent the disabled color. For this example, we’ll use black.
const styles = StyleSheet.create({ // ... appButtonDisabled: { backgroundColor: "#000" } });
Next, refactor the <AppButton />
component and use the useState
Hook to change the disabled state.
const AppButton = ({ title }) => { const [isDisabled, setDisabled] = useState(false); const handlePress = () => { setDisabled(true); setTimeout(() => setDisabled(false), 3000); }; return ( <TouchableOpacity onPress={handlePress} style={[ styles.appButtonContainer, isDisabled && styles.appButtonDisabled ]} disabled={isDisabled} > <Text style={styles.appButtonText}>{title}</Text> </TouchableOpacity> ); };
You can pass a disabled
prop to the TouchableOpacity
component to disable the onPress
behavior. You can pass an array of style objects to the style
prop.
When isDisabled
is set to true
, you can add the appButtonDisabled
property to the style
prop array using the &&
operator.
The above implementation disables the button for three seconds by displaying a different style, as shown below.
styled-components
styled-components
is a CSS-in-JS library that enables you to write each component with its own style and encapsulate the code in a single location.
React Native follows a certain specification for styling these components. For example, all CSS property names must be written in camelCase
— background-color
should be specified as backgroundColor
, border-width
as borderWidth
, etc.
This can be a little disorienting for a developer who is approaching mobile app development from a web development background. The styled-components
library enables you to write native CSS for styling a React Native component. Under the hood, styled-components
simply converts the CSS text into a React Native StyleSheet
object.
Run the following command to install styled-components
.
yarn add styled-components
Import the library.
import styled from "styled-components";
Refactor the code to implement styled-components
.
Replace the TouchableOpacity
and Text
components with ButtonContainer
and ButtonText
, respectively. These new components will be created using the syntax from styled-components
.
const AppButton = ({ onPress, title }) => ( <ButtonContainer onPress={onPress}> <ButtonText>{title}</ButtonText> </ButtonContainer> );
styled-components
uses tagged template literals to style the components using backticks (`). Each styled component must have a React Native component attached to it.
const ButtonContainer = styled.TouchableOpacity``; const ButtonText = styled.Text``;
Inside the backticks, add your CSS rules.
const ButtonContainer = styled.TouchableOpacity` elevation: 8; border-radius: 10px; padding-vertical: 10px; padding-horizontal: 12px; `; const ButtonText = styled.Text` font-size: 18; color: #fff; font-weight: bold; align-self: center; text-transform: uppercase; `;
Putting everything together, your code should look like this with the wrapper view for buttons:
import React from "react"; import { View, Button, TouchableOpacity, Text } from "react-native"; import styled from "styled-components"; TouchableOpacity.defaultProps = { activeOpacity: 0.8 }; const ButtonContainer = styled.TouchableOpacity` elevation: 8; border-radius: 10px; padding-vertical: 10px; padding-horizontal: 12px; background-color: #555; `; const ButtonText = styled.Text` font-size: 18px; color: #fff; font-weight: bold; align-self: center; text-transform: uppercase; `; const AppContainer = styled.View` flex: 1; justify-content: center; padding: 16px; background-color: #eee; `; const AppButton = ({ onPress, title }) => ( <ButtonContainer onPress={onPress}> <ButtonText>{title}</ButtonText> </ButtonContainer> ); const App = () => { return ( <AppContainer> <AppButton title="Hey there!"/> </AppContainer> ); }; export default App;
The above source code produces the following application using CSS-like syntax from the styled-components
library.
Emotion is also a CSS-in-JS library that supports styling React Native components, so you can use the @emotion-native
package for creating custom React Native buttons with CSS syntax. Emotion offers almost all features that styled-components
offers with very similar syntax.
Some other npm packages that wrap <TouchableOpacity/>
offer pre-built custom buttons for React Native with some features like icon support, theming, custom coloring, etc. But, those libraries are not very popular among developer communities due to the easiness of custom button creation and fully-featured UI kits. For example, you can build any custom button easily with the steps explained in this tutorial. On the other hand, you can use a fully-featured UI kit like Native Base, which offers many pre-built custom button styles.
As a developer, you must build UI components to match whatever reference or design your design team comes up with. It’s your responsibility to make the UI components look precisely as they’re outlined in your client’s plan or prototype.
There’s so much more you can do, but the steps in this guide will help you find your footing as you start creating custom UI components with React Native.
LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.
LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.
Start proactively monitoring your React Native apps — try LogRocket for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild a fast, real-time app with Relay 17 to leverage features like optimistic UI updates, GraphQL subscriptions, and seamless data syncing.
Simplify component interaction and dynamic theming in Vue 3 with defineExpose and for better control and flexibility.
Explore how to integrate TypeScript into a Node.js and Express application, leveraging ts-node, nodemon, and TypeScript path aliases.
es-toolkit is a lightweight, efficient JavaScript utility library, ideal as a modern Lodash alternative for smaller bundles.
2 Replies to "How to create and style custom buttons in React Native"
Wow..Well written. Thank you
Really helpful and comprehensive! Thanks very much.