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.

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:


_12
import React from 'react';
_12
_12
type ButtonProps = {
_12
label: string;
_12
onClick: () => void;
_12
};
_12
_12
const Button: React.FC<ButtonProps> = ({ label, onClick }) => {
_12
return <button onClick={onClick}>{label}</button>;
_12
};
_12
_12
export 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:


_14
type ButtonProps = {
_14
label: string;
_14
onClick: () => void;
_14
style?: React.CSSProperties;
_14
disabled?: boolean;
_14
};
_14
_14
const 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:


_16
type CardProps = {
_16
title: string;
_16
content: string;
_16
buttonLabel: string;
_16
onButtonClick: () => void;
_16
};
_16
_16
const 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.

Stay ahead of the pack 🐶

Join the newsletter to get the latest articles and expert advice directly to your inbox.
Totally free, no spam and you can unsubscribe anytime you want!

  • Expert Tips
  • No Spam
  • Latest Updates

I'll never share any of your information with a third party. That's a promise.

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:


_19
const 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:

  1. Create a reusable Widget Component:


    _15
    type WidgetProps = {
    _15
    title: string;
    _15
    content: React.ReactNode;
    _15
    onActionClick: () => void;
    _15
    };
    _15
    _15
    const Widget: React.FC<WidgetProps> = ({ title, content, onActionClick }) => {
    _15
    return (
    _15
    <div className="widget">
    _15
    <h3>{title}</h3>
    _15
    <div>{content}</div>
    _15
    <Button label="Take Action" onClick={onActionClick} />
    _15
    </div>
    _15
    );
    _15
    };

  2. Use the Widget in Your Dashboard:


    _16
    const Dashboard: React.FC = () => {
    _16
    return (
    _16
    <div className="dashboard">
    _16
    <Widget
    _16
    title="User Stats"
    _16
    content={<p>Total Users: 1,234</p>}
    _16
    onActionClick={() => alert("View Users")}
    _16
    />
    _16
    <Widget
    _16
    title="Sales"
    _16
    content={<p>Total Sales: $5,678</p>}
    _16
    onActionClick={() => 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

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.

1) True or False: A reusable component should always handle its own state internally.

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?

D is for danny

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.

Gobacktothetop

Made with 🐾 in 🏴󠁧󠁢󠁥󠁮󠁧󠁿

©2024 All rights reserved.