If you’ve ever found yourself duplicating code or struggling with managing a dozen different versions of the same component in your React applications, you’re in the right place.
The art of creating reusable components in React is a fundamental aspect of building efficient, maintainable, and scalable user interfaces but it comes with it’s own set of unique challenges.
Thankful; there are a series of idioms that exist the annals of computer science which seem to be mainly applicable everywhere.
Todays journey will be enriched with tips, practical examples, and real-world scenarios, ensuring that by the end of this article, you’re equipped to tackle component design like a seasoned professional.
Why Reusable Components?
It’s a great question.
First off, let’s clarify what we mean by “reusable components.” In React, a reusable component is a particular part of a User Interface that can be used multiple times in different parts of a project without needing to change its core logic. Think of it like a LEGO brick. There are bricks of all different shapes and sizes and each brick is designed to cover a very specific criteria, You which you can snap all different combinations into various constructs, but each brick still maintains its integrity, making your building process a lot smoother. In essence a component is a very small and specific module of code.
You might wonder why creating reusable components is important. Well, here are a few reasons:
Efficiency: Write once, use many times. This reduces the time spent on development.
Maintainability: It’s easier to manage and update one component than to tackle many versions of it.
Consistency: Reusable components ensure a consistent look and feel across your application.
Testing: Tracking down issues in a single component is far easier than within multiple copies.
Let’s break down the process and principles of building these shiny functional gems in React.
The Reusability Principle
Ideally, components should be easy to reuse. Sadly, here in the real world; this isn't always possible. Sometimes a component needs to provide additional functionality to cater for a bizarre edge case or the implementation of a provided component needs to be altered slightly to solve a particular use case.
A well designed component anticipates these modifications by providing “hooks” that allow users to extend or alter functionality without modifying the internal structure of the component.
Info
Components that depend on lots of other components are said to be “encumbered”. Encumbered components are difficult to reuse since all related components must also be imported for it to be effective.
Keep It Simple
Simplicity is key when designing reusable components. Aim for a single responsibility principle with the mantra being:
“One component should be responsible for one job.”
This makes it easier to understand, test, and reuse. Let’s look at a simple button component:
_12import React from 'react';_12_12type ButtonProps = {_12 label: string;_12 onClick: () => void;_12};_12_12const Button: React.FC<ButtonProps> = ({ label, onClick }) => {_12 return <button onClick={onClick}>{label}</button>;_12};_12_12export default Button;
In the example above, our Button component does just one thing: it renders a button with a specific label and triggers a function when clicked. Simple enough, right?
Make It Configurable
To maximise reusability, allow for customisation through props. Our Button can be improved by adding styles and types:
_14type ButtonProps = {_14 label: string;_14 onClick: () => void;_14 style?: React.CSSProperties;_14 disabled?: boolean;_14};_14_14const Button: React.FC<ButtonProps> = ({ label, onClick, style, disabled }) => {_14 return (_14 <button onClick={onClick} style={style} disabled={disabled}>_14 {label}_14 </button>_14 );_14};
Now, this button can adapt to various contexts by just passing different styles or a disabled state. It's more versatile while maintaining the core functionality.
Embrace Composition
React components shine when they play together. Instead of building a monolithic component with a lot of functionality, think about composing smaller components together. For instance, you could create a Card
component that utilises our Button
component:
_16type CardProps = {_16 title: string;_16 content: string;_16 buttonLabel: string;_16 onButtonClick: () => void;_16};_16_16const Card: React.FC<CardProps> = ({ title, content, buttonLabel, onButtonClick }) => {_16 return (_16 <div className="card">_16 <h2>{title}</h2>_16 <p>{content}</p>_16 <Button label={buttonLabel} onClick={onButtonClick} />_16 </div>_16 );_16};
By composing Button
inside Card
, we’ve built something more complex without sacrificing the reusability of our components.
Decoupling State
When it comes to reusability, managing state within a component can create tight coupling and make a component less flexible. Instead, aim to handle state in parent components and pass the required data down through props. Consider a form component that allows for input:
_19const Form: React.FC = () => {_19 const [inputValue, setInputValue] = React.useState('');_19_19 const handleSubmit = (event: React.FormEvent) => {_19 event.preventDefault();_19 console.log(inputValue); // Do something with the input value_19 };_19_19 return (_19 <form onSubmit={handleSubmit}>_19 <input_19 type="text"_19 value={inputValue}_19 onChange={(e) => setInputValue(e.target.value)}_19 />_19 <Button label="Submit" onClick={handleSubmit} />_19 </form>_19 );_19};
In this example, the Form
manages the input state, while the Button
remains a reusable, stateless component.
Putting Ideas Into Practice
Let’s take a look at how this all ties together with a practical example. Imagine you’re building a dashboard for an application with various widgets. Each widget might require a title, some content that could be static or dynamic, and buttons for actions. Here’s how you can use the concepts mentioned:
-
Create a reusable
Widget
Component:_15type WidgetProps = {_15title: string;_15content: React.ReactNode;_15onActionClick: () => void;_15};_15_15const Widget: React.FC<WidgetProps> = ({ title, content, onActionClick }) => {_15return (_15<div className="widget">_15<h3>{title}</h3>_15<div>{content}</div>_15<Button label="Take Action" onClick={onActionClick} />_15</div>_15);_15}; -
Use the
Widget
in Your Dashboard:_16const Dashboard: React.FC = () => {_16return (_16<div className="dashboard">_16<Widget_16title="User Stats"_16content={<p>Total Users: 1,234</p>}_16onActionClick={() => alert("View Users")}_16/>_16<Widget_16title="Sales"_16content={<p>Total Sales: $5,678</p>}_16onActionClick={() => alert("View Sales")}_16/>_16</div>_16);_16};
As you can see, using reusable components makes it incredibly easy to modify the dashboard without writing repetitive code. Changes to styling or logic need only happen in one place.
Keep the User in Focus
Tip
Remember always to consider the user experience, as software engineering isn’t just about the code; it’s about delivering real value to users.
User experience is paramount in software engineering. A well-crafted component doesn’t just need to function; it needs to resonate with users.
When designing components it’s important to understand the problem you’re attempting to solve while creating it
Some helpful prompts you might give yourself are:
- Is it intuitive?
- Does it follow accessibility guidelines?
- Are the interactive elements easily clickable and visible?
Keeping these questions at the forefront of your decision-making process allows you to create a product that’s not only well-engineered but also genuinely valuable and pleasant to use.
Don’t shy away from refactoring your components and continue experimenting to master the subtle art of writing clean and modular code.
Reinforce Your Learning
One of the best ways to reinforce what your learning is to test yourself to solidify the knowlege in your memory.
Complete this 10 question quiz to see how much you remember.
Thanks alot for your feedback!
The insights you share really help me with improving the quality of the content here.
If there's anything you would like to add, please send a message to:
[email protected]Was this article this helpful?
About the author
Danny Engineering
A software engineer with a strong belief in human-centric design and driven by a deep empathy for users. Combining the latest technology with human values to build a better, more connected world.