When it comes to adding a button in React Native, your decision often boils down to a single question: how much control do you need? The built-in Button component is fine for quick prototypes or a simple alert, but it offers almost zero styling flexibility. For any real-world app, you'll find yourself reaching for TouchableOpacity or, more likely, the modern and more powerful Pressable component.
Choosing the Right Interactive Component
As you start building out your app's interface, one of the first decisions you'll make is which "touchable" to use. React Native gives you a few core options, but they aren't created equal. The component you pick directly impacts your app's design flexibility, user experience, and how easy it is to maintain down the road.
The Old Guard: Button and TouchableOpacity
First up is the basic Button. It’s incredibly simple to use, but that’s where the benefits end. You get a title, an onPress handler, and a color prop for the text on Android—and that’s about it. You can't add icons, use custom fonts, or wrap complex layouts inside it. This makes it a poor choice for anything beyond a proof-of-concept.
For years, TouchableOpacity was the go-to for custom buttons. It’s a wrapper component that lets you turn any <View> into a tappable element. When a user presses it, the component's opacity fades slightly, giving clear visual feedback. It's reliable and has served the community well for creating all sorts of custom-styled buttons.
The Modern Standard: Pressable
These days, however, Pressable is the component you should be using. It was introduced as a more capable and extensible foundation for handling all user interactions, and it quickly became the superior choice for a few key reasons. It gives you a much richer event system right out of the box, including handlers for onPressIn, onPressOut, and onLongPress.
The real game-changer with
Pressableis its ability to directly respond to interaction states. You can define styles for when an element is being actively pressed, which is a much cleaner approach than managing state manually withTouchableOpacity.
As we look toward 2026, the community consensus is clear: Pressable is a best practice. Recent tutorials and benchmarks from the team behind Meta's React Compiler even show that switching to Pressable can result in a 2.5x faster interaction feel in production tests without increasing memory usage. That's a huge win for UI responsiveness. If you want to dive deeper, you can explore some of the top developer tips for modern React Native.
To help you choose, here's a quick breakdown of how these components stack up against each other.
React Native Button Components at a Glance
This table compares React Native's core touchable components to help you decide which one best fits your needs.
| Component | Custom Styling | Event Handling | Best For |
|---|---|---|---|
Button | Almost none. Limited to a single text color. | Basic onPress only. | Quick prototypes, simple alerts, or temporary placeholders. |
TouchableOpacity | Fully customizable. Can wrap any View. | Basic onPress and onLongPress. | Creating simple custom buttons with a fade-on-press effect. |
Pressable | Fully customizable. Supports styles for pressed state. | Rich events: onPressIn, onPressOut, onLongPress. | All new development. Complex interactions and stateful styling. |
Ultimately, Pressable is designed to be the future-proof foundation for creating any interactive element in your app.
Making the Right Choice
To make it even simpler, just think about what your UI needs.
- Need a barebones, unstyled button? The core
Buttonmight do the job for a moment. - Need a custom design with a simple tap effect?
TouchableOpacitystill works. - Need anything else? If you need different visual states (like a darker color on press), hover effects on web, or advanced press events,
Pressableis the clear winner.
This flowchart visualizes that exact decision-making process.

As you can see, the moment your design calls for custom styles or more sophisticated event handling, all roads lead to Pressable. It’s the component you’ll want to build with from the start.
Okay, you've seen why Pressable is the go-to for building interactive elements. Now, let's put that knowledge to work and build a truly reusable button component.
In any serious React Native project, creating a centralized, custom button is one of the first things you should do. It's a game-changer for maintaining a consistent look and feel, and it saves you from rewriting the same styling and logic over and over again. When you need to update the design, you change it in one place, and it's done.
We're going to build a CustomButton that’s ready for real-world use. It’ll be flexible enough to handle not just text and a press event, but also different visual states.
Setting Up the Basic Structure
First things first, let's get the boilerplate out of the way. In your components folder, create a new file, CustomButton.js.
The heart of our component is a Pressable wrapping a Text element. We'll start with the two most essential props: title for the button's label and onPress for the function that runs when it’s tapped.
import React from 'react';
import { Pressable, Text, StyleSheet } from 'react-native';
const CustomButton = ({ onPress, title }) => {
return (
{title}
);
};
const styles = StyleSheet.create({
// We'll add styles here soon
});
export default CustomButton;
This is a solid starting point. It works, but a great button in react native needs to do more. It has to give users clear feedback and handle common states like loading or being disabled.
Integrating Icons and Loading States
Modern apps rarely use text-only buttons. Pairing text with an icon is a simple way to improve clarity. We can easily add this by introducing an iconName prop. For this, the react-native-vector-icons library is a fantastic and widely-used choice.
Buttons also often trigger actions that aren't instant, like an API call. You need a loading state to show the user that something is happening and to prevent them from tapping the button again. We'll add a loading prop and show an ActivityIndicator.
Let's expand our component to handle a few more props:
iconName: A string for the icon you want to display.loading: A boolean that, when true, shows a spinner instead of the text and icon.disabled: A boolean to turn off the button'sonPressand give it a "faded out" look.
Here's how we can weave these new features into the component.
// … imports
import { ActivityIndicator } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
const CustomButton = ({ onPress, title, iconName, loading, disabled }) => {
const buttonStyles = [
styles.button,
disabled && styles.disabledButton, // Apply disabled style
];
return (
<Pressable style={buttonStyles} onPress={onPress} disabled={disabled || loading}>
{loading ? (
) : (
<>
{iconName && }
{title}
</>
)}
);
};
By building state management directly into the component—like handling
loadinganddisabledprops—you create a self-contained and predictable UI element. This prevents logic from leaking into parent components and keeps your codebase clean and scalable.
With this setup, you have a powerful and versatile button. You can quickly make a primary button with a "save" icon or a form submission button that shows a loading indicator.
Mastering this component-based approach is fundamental to building efficient apps. If you want to take it a step further, check out our guide on how to create and publish your own React Native libraries and components. This is the architecture that will let your projects scale.
Nailing the Platform-Specific Button Feel

A great app doesn't just work on every device; it feels right on every device. When you're building a custom button in react native, that means paying attention to the subtle but crucial differences between iOS and Android. These platform-specific interactions are what make an app feel intuitive and polished, rather than just a generic port.
The Android Ripple Effect
On Android, users expect to see the Material Design ripple effect—that satisfying visual feedback that spreads out from their fingertip. It's a small detail, but it makes the entire experience feel more native and responsive.
Getting this right is surprisingly simple with the Pressable component. All you need is the android_ripple prop. You can pass an object to this prop to configure the ripple's color, radius, and even let it expand beyond the button's edges by setting borderless to true.
Here’s a quick example:
import { Pressable, Text, StyleSheet } from 'react-native';
const AndroidButton = () => (
<Pressable
onPress={() => console.log('Pressed!')}
style={styles.button}
android_ripple={{ color: 'rgba(255, 255, 255, 0.3)' }}
<Text style={styles.text}>Tap Me on Android</Text>
);
With just that one line, your button feels instantly at home on any Android device. It's a tiny change that has a huge impact on the perceived quality of your app.
Crafting the iOS Touch Experience
Things are a bit different over on iOS. Users there are accustomed to a subtle highlight or a quick fade in opacity when they tap a button. We can mimic this classic iOS behavior perfectly by tapping into Pressable's state-based styling.
The style prop can accept a function that gives you a pressed state. You can then use this boolean to serve up different styles when the button is actively being touched. It's the ideal way to create that familiar iOS feedback, like darkening the background or reducing the opacity slightly.
Using the
pressedstate inside thestyleprop is the key to creating dynamic visual feedback. This is a core part of Apple's Human Interface Guidelines, and it makes your app's interactions feel more direct and responsive.
This simple function gives you total control to fine-tune the touch feedback. Whether you want a gentle dimming effect or a more obvious color shift, you can dial it in to match user expectations on Apple devices.
Handling Both Platforms in One Component
So, how do you manage both of these behaviors without cluttering your code? React Native gives you a couple of fantastic ways to handle platform-specific code.
For small, inline style changes, the Platform module is your best friend. The Platform.select method lets you define an object with ios and android keys, and it automatically picks the right style for the running platform. It's perfect for things like setting different background colors.
import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
button: {
// … common styles
…Platform.select({
ios: {
backgroundColor: '#007AFF', // Classic iOS blue
},
android: {
backgroundColor: '#6200EE', // Material Design purple
},
}),
},
});
When your logic gets more complex—maybe you need entirely different layouts or internal components—it's time to reach for platform-specific file extensions.
You can create two files:
CustomButton.ios.jsCustomButton.android.js
When you import ./CustomButton, React Native’s bundler is smart enough to grab the correct file for the platform you’re on. This keeps your platform-specific code cleanly separated and your main component file much easier to read.
Optimizing Button Performance in Lists

A button in react native will almost never just float on a static screen. Most of the time, you'll find them inside long, scrollable lists. This is exactly where performance starts to matter—a lot. A choppy FlatList with buttons that hesitate or lag makes your entire app feel clunky and unresponsive.
The most common trap I see developers fall into is creating wasteful re-renders. Think about it: every time your list component re-renders (even for a minor state change), it generates a brand-new onPress function for every single button.
From React's perspective, a new function is a new prop. It sees this change and forces the button's list item to re-render, even if visually, nothing has changed. When you have dozens or even hundreds of items, this creates a performance bottleneck that can grind your UI to a halt.
Luckily, the fix is straightforward: memoization.
Preventing Unnecessary Renders with useCallback
The useCallback hook is your go-to solution here. When you wrap your onPress handler in useCallback, you're telling React, "Don't recreate this function on every render. Keep using the same one unless its dependencies have changed."
This simple change is a game-changer for list performance. It ensures the onPress prop stays stable, which signals to the FlatList that your button component doesn't need to be re-rendered unnecessarily.
// Inside your list item component
const handlePress = useCallback(() => {
// Your logic here, using the item's id
console.log(Button pressed for item ${id});
}, [id]); // Only re-create the function if the id changes
return ;
If you're looking to really master list optimization, our guide on advanced React Native ListView examples digs deeper into rendering strategies that work perfectly with performant buttons.
A new function on every render equals a new prop. Memoizing
onPresswithuseCallbackis one of the most effective optimizations you can make when using buttons inside lists.
Deferring Work with the Intersection Observer
For truly high-performance lists with hundreds or thousands of items, you can push optimization even further. Why do work for a button the user can't even see? This is where React Native’s Intersection Observer API comes into play.
This API is a powerful tool that lets you know when a component enters or leaves the screen. It's perfect for things like:
- "Load More" buttons that only fetch data when they become visible.
- Analytics events that only fire when a user actually sees the button.
- Lazy-loading heavy components tied to a button press.
This approach is incredibly efficient. Instead of attaching a heavy function or listener to every single button in a massive list, you only trigger the logic when it's needed. Since React Native 0.83, this API has been fine-tuned for mobile, cutting down unnecessary renders by up to 70% in some FlatList scenarios.
Benchmarks from the Hermes V1 engine show these optimizations can lead to 7.6% faster time-to-interactive on Android and 9% on iOS, making your buttons feel more responsive from the get-go. You can read more about these updates and what's new in the latest React Native versions.
Designing Accessible Buttons for All Users
Let's be honest: a button that some people can't use isn't just a poor design choice, it's a broken feature. When we build a button in react native, we have to think beyond the visual. Making it accessible isn't an afterthought or a "nice-to-have"—it’s a fundamental part of creating an app that actually works for everyone.
If a button isn't built with accessibility in mind, you're shutting out users who rely on assistive technologies. An accessible button clearly communicates its purpose, not just with a pretty icon or color, but to screen readers. This is how users with visual impairments can confidently navigate your app. It’s all about creating an equivalent experience for every single person.
Essential Accessibility Props
React Native gives us a solid set of tools to get this right. For any custom button you build, there are three accessibility props you absolutely must use: accessibilityLabel, accessibilityHint, and accessibilityRole.
accessibilityLabel: This is your top priority. It's the text a screen reader will announce. If your button is just an icon—say, a gear for settings—this label is the only way a user with a visual impairment will know its function. Keep it short and to the point, like "Open user settings."accessibilityHint: Think of this as providing a little extra context. It explains what happens after the user taps the button. For a label like "Add to cart," a helpful hint could be, "Adds this item to your shopping basket and takes you to the cart."accessibilityRole: This prop tells the assistive technology what kind of element it's dealing with. For any button, you should always set this to'button'. This signals to the user that it's an interactive element they can tap.
Getting these props right transforms your UI from something that just looks good into an experience that can be understood through sound and touch. This is the bedrock of mobile accessibility.
Touch Target Size Matters
Accessibility isn't just about screen readers; it also involves physical interaction. Both Apple's Human Interface Guidelines and Google's Material Design have strict rules on this, and for good reason. They both mandate a minimum touch target size of at least 44×44 points.
That number isn't random. It’s based on years of research into how people actually hold and use their phones. A larger target ensures that users with motor impairments, or even just someone rushing to catch a train, can tap your buttons reliably without getting frustrated. A tiny button is a dead-end for many users.
When you're styling a Pressable, you can easily enforce this by setting minWidth and minHeight. It's a simple step that makes a world of difference.
For a deeper dive into this, check out our guide on enhancing user experience with dark mode and accessibility, where we cover more strategies for building truly inclusive apps.
The Business Case for Better Buttons

As an engineering manager or founder, you're constantly looking at the big picture. While your developers are deep in the code, you're focused on how every decision impacts the business. It’s easy to dismiss a button in React Native as a minor detail, but a well-built one is a powerful tool that directly affects your budget, timeline, and user satisfaction.
Think of it this way: investing in a solid, reusable component library from day one is a strategic move. Instead of paying to build and maintain two separate native apps for iOS and Android, React Native's shared codebase gives you a massive head start. It’s a proven way to gain a competitive edge by simply getting to market faster and more efficiently.
Slashing Costs and Accelerating Timelines
The cost savings are not just theoretical. Across the U.S., enterprise teams using React Native have cut development costs by 40-50% compared to the traditional two-app approach. For any team, but especially startups, this means you can launch on both iOS and Android for what it might have cost to build for just one. You can find more real-world data on the cost-saving benefits of using React Native in various projects.
Just look at Ticket Hoy's ticket validation app. They had a critical need for an ultra-fast scanning process to keep lines from getting out of control at events. By choosing React Native, they built an app for both platforms that worked online and off, all while cutting their development timeline by an estimated 60-70%. Their buttons gave instant, clear feedback, which was absolutely essential in that high-pressure environment.
A high-quality button is a silent partner in your business operations. In a checkout flow, it boosts conversions. In an internal tool, it improves employee efficiency. In a ticket-scanning app, it prevents customer frustration. The ROI of good button design is measured in satisfaction and speed.
Strategic Investment for Long-Term Growth
Ultimately, prioritizing high-quality components from the start is a business strategy. A performant, accessible button in a critical flow—like a purchase screen or a login form—directly impacts user retention. When your team has a well-designed component system to work with, they build faster, more consistent products.
This isn't just an expense; it's a foundational investment in your company's long-term growth and profitability. That simple tap on a screen is connected to your bottom line in more ways than you might think.
Common Questions About React Native Buttons
As you get your hands dirty building out your app, you're bound to hit a few snags with buttons. It happens to everyone. Here are a few of the most common headaches I see developers run into, along with the straightforward, expert solutions to get you moving again.
How Do I Create a Button with a Gradient Background?
This one comes up a lot. React Native's built-in components don't handle gradients on their own. The best and most common solution is to pull in a small, well-trusted library like react-native-linear-gradient.
All you have to do is wrap the children of your Pressable component with the <LinearGradient> component from the library. You'll pass it a colors array—something like ['#ff7e5f', '#feb47b']—to set the gradient's look. You can even control the angle with the start and end props. It's a really simple way to create some eye-catching designs without a lot of fuss.
Why Is My Button's OnPress Firing Multiple Times?
Ah, the classic "double-tap" bug. You usually see this on slower devices where a user can tap the button again before the first action has completed. The most reliable way to prevent this is by managing a loading or submitting state.
The trick is to have your
onPresshandler immediately set a loading state totrue. Pass that state directly to the button'sdisabledprop. Once your asynchronous task, like an API call, is done, make sure you set the loading state back tofalseinside afinallyblock. This ensures the button gets re-enabled, even if the request fails.
This little pattern completely bulletproofs your forms against frustrating duplicate submissions.
What Is the Best Way to Add Haptic Feedback?
While you could use the basic Vibration API, it’s a bit of a blunt instrument. For a much more polished user experience, I always recommend the react-native-haptic-feedback library. It gives you access to specific, nuanced feedback types like 'impactLight' or 'notificationSuccess'.
Here’s a pro tip: trigger the haptic feedback on the onPressIn event instead of onPress. This gives the user an immediate physical response the very moment their finger touches the screen, making the entire interface feel snappy and incredibly satisfying.
At React Native Coders, we deliver timely news, in-depth tutorials, and the strategic insights you need to build faster and smarter. Stay ahead of the curve with our expert analysis on everything from performance to security. Explore more at https://reactnativecoders.com.





















Add Comment