How to Build a Smart Button in React: Creating Powerful UI Components
React has revolutionized frontend web development, providing developers with a component-driven model to build interactive user interfaces. One of the fundamental building blocks of any web app UI is the humble button. However, with React, we can create "smart buttons" that go beyond simple HTML buttons by incorporating custom logic, state management, and advanced functionality.
In this post, we‘ll examine what makes a button "smart", explore how to build smart buttons in React, and see examples of the cool features smart buttons can provide. By the end, you‘ll be equipped to build your own smart buttons and take your React UI development to the next level!
What is a Smart Button?
A smart button is a reusable React component that encapsulates logic and state relating to a button element. Rather than just being a clickable HTML <button>
element that triggers a function, a smart button can:
- Modify its own appearance and behavior based on its current state
- Enable or disable itself under certain conditions
- Perform asynchronous actions like API calls when clicked
- Provide feedback to the user while an action is being performed
- Open modals, trigger navigation, or control other components
The beauty of building buttons as React components is that they are composable and reusable across your app. You can configure their functionality with props, separate their logic from other components, and easily drop them into any part of your UI that requires their capabilities.
Anatomy of a Basic Smart Button
Let‘s start by looking at a basic example of a smart button component. We‘ll create a SmartButton
that maintains its own isClicked
state, modifying its appearance and logging a message when clicked.
import React, { useState } from ‘react‘;
function SmartButton() {
const [isClicked, setIsClicked] = useState(false);
const handleClick = () => {
setIsClicked(true);
console.log(‘Button clicked!‘);
};
return (
<button
onClick={handleClick}
style={{ backgroundColor: isClicked ? ‘green‘ : ‘blue‘ }}
>
{isClicked ? ‘Clicked!‘ : ‘Click me‘}
</button>
);
}
export default SmartButton;
Let‘s break this down:
- We create a functional component called
SmartButton
- Inside the component, we declare a state variable called
isClicked
using theuseState
hook, initialized tofalse
- We define a
handleClick
function that will be called when the button is clicked. This function setsisClicked
totrue
and logs a message - In the component‘s JSX, we render a
<button>
element:- We pass
handleClick
to the button‘sonClick
prop so it will be called on click - We set the button‘s background color based on the current
isClicked
state using an inline style - We set the button‘s text to "Clicked!" when
isClicked
istrue
, or "Click me" otherwise
- We pass
We‘ve created a button that modifies its own state and appearance when clicked – our first smart button! This component can now be imported and used anywhere in our app.
While this example is quite basic, it demonstrates the key aspects of a smart button: it manages its own state, updates its appearance based on that state, and performs actions when clicked. Now let‘s look at some more advanced functionality we can add.
Adding Props for Configurability
One big benefit of making our button a reusable component is that we can pass props to configure it for different use cases. Let‘s modify our SmartButton
to accept a text prop to customize the button‘s label.
function SmartButton({ text }) {
const [isClicked, setIsClicked] = useState(false);
const handleClick = () => {
setIsClicked(true);
console.log(`${text} button clicked!`);
};
return (
<button
onClick={handleClick}
style={{ backgroundColor: isClicked ? ‘green‘ : ‘blue‘ }}
>
{isClicked ? `${text} clicked!` : text}
</button>
);
}
Now when we use this component, we can pass in a text
prop to set the button‘s label, like so:
<SmartButton text="Buy Now"/>
<SmartButton text="Read More"/>
This makes our smart button more flexible and reusable. We can take this further by adding props to control the button‘s styling, initial state, behavior, and more. However, be careful not to over-parameterize your component with too many props, as it can become difficult to understand and maintain. Try to keep props focused on essential configuration.
Conditional Rendering and Disabling
Another useful feature for buttons is the ability to be disabled under certain conditions. For instance, we may want to disable a form submission button when the form‘s inputs are invalid, or disable a delete button when no item is selected.
Let‘s add a disabled
prop to control whether our button is enabled or not.
function SmartButton({ text, disabled }) {
const [isClicked, setIsClicked] = useState(false);
const handleClick = () => {
if (!disabled) {
setIsClicked(true);
console.log(`${text} button clicked!`);
}
};
return (
<button
onClick={handleClick}
disabled={disabled}
style={{
backgroundColor: disabled ? ‘gray‘ : isClicked ? ‘green‘ : ‘blue‘,
opacity: disabled ? 0.5 : 1,
cursor: disabled ? ‘not-allowed‘ : ‘pointer‘
}}
>
{isClicked && !disabled ? `${text} clicked!` : text}
</button>
);
}
We‘ve made a few key changes here:
- The component now accepts a
disabled
prop to control if the button is enabled - In the
handleClick
function, we first check if!disabled
before updating state and logging the click, so clicks have no effect when disabled - We pass the
disabled
prop to the<button>
‘sdisabled
attribute to enable the browser‘s built-in disabled button behavior and styling - We modify the inline
style
object to apply a gray background color, reduced opacity, and a "not-allowed" cursor whendisabled
is true
Now our button respects and visually indicates its disabled
state. We can use this prop to control the button‘s enabled state based on the conditions in the parent component.
Async Actions and Loading State
Often, buttons are used to trigger asynchronous actions like submitting forms or fetching data. In these cases, it‘s important to communicate to the user when an action is in progress. We can add a loading
state to our button to show a loading indicator while an async action is being performed.
Let‘s modify our button to accept an onClick
callback prop that can perform an asynchronous action, showing a loading state until that action completes.
function SmartButton({ text, disabled, onClick }) {
const [isLoading, setIsLoading] = useState(false);
const handleClick = async () => {
if (!disabled) {
setIsLoading(true);
await onClick();
setIsLoading(false);
}
};
return (
<button
onClick={handleClick}
disabled={disabled || isLoading}
style={{
opacity: (disabled || isLoading) ? 0.5 : 1,
cursor: (disabled || isLoading) ? ‘not-allowed‘ : ‘pointer‘
}}
>
{isLoading ? ‘Loading...‘ : text}
</button>
);
}
In this version:
- We replace the
isClicked
state with anisLoading
state to track when an async action is in progress - The component accepts an
onClick
prop, expected to be an async function to be called when the button is clicked - In
handleClick
, after checking!disabled
, we setisLoading
totrue
, await theonClick
callback, then setisLoading
back tofalse
- We disable the button and apply styling changes when either
disabled
orisLoading
is true - We show a "Loading…" message when
isLoading
is true
Now our button can handle asynchronous actions and provide visual feedback to the user while those actions are processing.
Wrapping Up
We‘ve seen how to build a basic smart button in React and explored some of the powerful features and behaviors we can add through props and state. By encapsulating this functionality within a reusable component, we can create consistent, robust buttons that can be plugged in across our application.
Some additional ideas to continue leveling up your smart buttons:
- Allow button customization through props for color, size, icon, etc.
- Incorporate accessibility attributes and keyboard navigation
- Integrate with form libraries for validation and submission
- Debounce rapid clicks to prevent accidental double-submits
- Add tooltips to provide more context on hover
- Incorporate analytics to track button engagement
The key to building effective smart buttons is to balance flexibility and simplicity. Aim to create focused components that do one thing well, and compose them together to build out more complex UI features.
By leveraging the power and composability of React, you can build a library of smart buttons and other UI components that accelerate your development and ensure a polished, engaging user experience. Now go forth and build some brilliant buttons!