DEV Community

Cover image for Writing clean, reusable components in React (best practices)
Programming with Shahan
Programming with Shahan

Posted on • Updated on • Originally published at freecodecamp.org

Writing clean, reusable components in React (best practices)

πŸ”‘ Key Concepts

What are reusable React components? You can think of them as building blocks.

They are independent pieces of code that can be reused throughout your website to save you time and effort.

They can be anything from simple buttons to complex forms.

Why Use Reusable Components?

As your website grows, you can easily add new features by combining existing components. This makes your code more scalable and adaptable.

You can use your reusable piece of code in future projects without writing it again from scratch.


🧩 How to Write Clean, Reusable React Components

Here are the two most important things to keep in mind while writing clean reusable React components:

Image of react reusable component

1. 🩼Avoid Side Effects. Don't put logic that interacts with external data (like making API calls) directly inside a reusable component. Instead, pass this logic as props to the component.

For example, if a button does more than just looking pretty, like fetching data from the internet, it might not be reusable.

I am not going to show you an example of this until you grasp the concept of passing props with best practices. Keep reading.

This is a reusable button component. But it lacks best practices. I will show you why in the example section.

// This is a reusable button component (bad practice!)
const Button = () => {
  return (
    <button> Click Me </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

‍
2. πŸ—ƒοΈ Use Props. Props are arguments you pass to a component to customize its behavior and appearance. This allows you to use the same component for different purposes.

// This is a button component that can change its color
const Button = ({ color }) => {
  return (
    <button style={{ backgroundColor: color }}> Click Here </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

This is still a bad practice because you have a fixed label called "Click Here". If you want to change the text on your button, let say - "Sign Up", then you would have to go back to the button component and make that change.

That means every time we want to use a different text, we'd have to go back and edit the code. In other words, it's no longer reusable.

πŸ’ͺ Challenge: So what's the solution?

You already have the answer. But if you don't, I am going to show you in the example section.

🌴 Hint: Think about how you might want to use the component in different situations and design it to be flexible and adaptable.

πŸƒExamples of Reusable React Components

Here are some common examples of reusable React components, along with some code examples to get you started:

1. Buttons: Basic buttons with different styles and functionalities.

// Button component
import React from "react";

const Button = ({ color, label, onClick }) => {
  return (
    <button
      className={`padding-2 shadow-none hover:shadow background-light-${color} hover:background-dark-${color}`}
      onClick={onClick}
    >
      {label}
    </button>
  );
};

export default Button;

// Using the Button component
<Button color="blue" label="Click Here" onClick={() => console.log("Button clicked!")} />
Enter fullscreen mode Exit fullscreen mode

As you can see, I did not write "Click Here" in the button component. I want to make my button reusable, and thus it doesn't know anything about custom styles or texts.

So, I passed them as props (e.g., color, label, onClick) to change them in the future without touching the original button components. Hope that makes it clear.

πŸͺœSolution: You need to pass EACH functionality as props in the reusable component - that's it.

2. Navbars: Navigation bars that provide consistent navigation across your website.

// Navbar component
import React from "react";

const Navbar = ({ isLoggedIn }) => {
  return (
    <div className="navbar">
      <div className="navbar-container">
        <div className="navbar-logo">
          <img src={logo} alt="logo" />
        </div>
        <div className="navbar-links">
          <a href="/">Home</a>
          <a href="/about">About</a>
          <a href="/contact">Contact</a>
          {isLoggedIn ? (
            <a href="/profile">Profile</a>
          ) : (
            <a href="/login">Login</a>
          )}
        </div>
      </div>
    </div>
  );
};

export default Navbar;

// Using the Navbar component
<Navbar isLoggedIn={true} />
Enter fullscreen mode Exit fullscreen mode

As you can see, I passed <Navbar isLoggedIn={true} />

This line utilizes the Navbar component and passes the isLoggedIn prop with a value of true, indicating the user is logged in. This will display the "Profile" link and hide the "Login" link.

Similar to the button component, the Navbar component is reusable and accepts props to customize its behavior. Perfect!

3. Why API call in button component is a bad practice

Now, you understand everything about reusable component in React.

Let's dig deeper by solving a complex problem.

Consider the scenario, where you have a button that does an API call. The code for the button component can be the following:

import React from "react"; 
import doAPICall from "../api"

const SaveButton  = () => {
  return (
    <button
      onClick={() => {
        doAPICall();
      }}
    >
      Save
    </button>
  );
}

export default SaveButton 
Enter fullscreen mode Exit fullscreen mode

It is quite clear that you can’t reuse the above button in multiple places as this button component contains a side-effect (doAPICall()) inside it.

To make this component reusable, first, you will have to extract out the side-effect and pass that as a prop to the button component like the following:

const App = () =>  {
  function doAPICall() {
    // Does an API call to save the current state of the app.
  }

  return (
    <div>
      <SaveButton onClick={doAPICall}/>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

The button component will look like the following:

const SaveButton  = ({
  onClick
}) => {
  return (
    <button
      onClick={onClick}
    >
      Save
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

As you can see, the above button can now be reused in all places where you want to save data on click of a button. The button can now be used like this in multiple places:

const App = () =>  {
  function saveUser() {
    // Does an API call to save the user.
  }

  function saveProject() {
    // Does an API call to save the project.
  }

  return (
    <div>
      <SaveButton onClick={saveUser}/>
      <SaveButton onClick={saveProject}/>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

You can also make the button component more reusable by using a prop to control the label like the following:

const App = () =>  {
  function saveUser() {
    // Does an API call to save the user.
  }

  function saveProject() {
    // Does an API call to save the project.
  }

  return (
    <div>
      <SaveButton onClick={saveUser} label="Save user"  />
      <SaveButton onClick={saveProject} label="Save project" />
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

The button component will look like the following:

const SaveButton  = ({
  onClick,
  label
}) => {
  return (
    <button
      onClick={onClick}
    >
      {label}
    </button>
  );
}

Enter fullscreen mode Exit fullscreen mode

πŸ‡ Recommendation

Did you know that the majority of developers and UI/UX designers use a fantastic design tool called Figma to create web design components and mockups/prototypes for websites or apps? It became the most popular design tool (RIP Adobe XD) for developers due to its all-in-service:

  • βš“ Super Clean UI Design (Figma)
  • 🎈 Collaboration with Other Team Members (FigJam)
  • πŸ‘¨β€πŸ’» Developer Mode (Figma Pro)
  • πŸ€– Creating AI UI/UX Templates in no time (FigJam AI & Plugins)

Image of figma

As a frontend developer, having some design skills is required since job recruiters often seek candidates with such capabilities. Fortunately, with FigJam AI, you don't need to master advanced design skills. FigJam AI offers tips to enhance your designs, automates tedious tasks, and best of all, it's currently available for free to everyone.

Image of frontend job requirements


πŸ‘Conclusion

Congratulations! You've successfully learned how to build clean reusable React component with BEST practices.

Remember, reusable components are the building blocks of robust React development. By practicing reusable components, you can build cleaner, more efficient, and more maintainable React applications.

The more you practice, the better you'll become at identifying opportunities to use them in your projects!

If you like this article, you may also like my 𝕏 account for more lessons on frontend development.

Read More: The Future of Frontend Development

Top comments (20)

Collapse
 
seandinan profile image
Sean Dinan

Very good write-up! One thing I would add in terms of reusability is render props. For example, if you have a generic button that renders a logo and a label, you can maximize reusability with a renderIcon prop.

const IconButton = ({ label, renderIcon, onClick }) => (
  <button onClick={onClick}>
    <span style={{ marginRight: '1.5rem' }}>
      {renderIcon(styles.buttonIcon)}
    </span>
    {label}
  </button>
)
Enter fullscreen mode Exit fullscreen mode
/* ...other component code */
<IconButton label="Save" renderIcon={(style) => <CheckIcon style={style}/>}/>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
codewithshahan profile image
Programming with Shahan

Thanks for sharing. I tried my best to keep this article as simple as possible focusing purely on beginners.

There are more advanced concepts for reusable components in React. Most of the time, we use Hooks in React for building reusable comp. Actually,they were introduced as a way to write reusable and stateful logic in functional components without needing class components.

But I believe, beginners should understand the React fundamental concepts pretty clearly first before jumping into advanced topics. That is what the article is about.

Collapse
 
abhay_a_joshi profile image
Abhay Joshi

Thanks for writing this great article. As you mentioned this is focused for beginners, may be, title also can include that information.

Collapse
 
fpaghar profile image
Fatemeh Paghar

The discussion on why incorporating API calls directly into button components is a bad practice is insightful. The step-by-step solution presented, extracting side effects and passing them as props, showcases a thoughtful approach to handling complex scenarios, ensuring that components remain truly reusable.

The examples provided, especially those related to buttons and navigation bars, effectively illustrate how to design components for maximum reusability. The emphasis on passing each functionality as props allows for flexibility and adaptability, crucial in diverse web development scenarios.

Overall, a well-crafted and informative article! πŸ‘

Collapse
 
codewithshahan profile image
Programming with Shahan

I appreciate your detailed feedback

Collapse
 
ekeijl profile image
Edwin • Edited

Nice article! I would like to add:

  • It can be difficult to define what the responsibilities of the component are. You will need to be very strict about the API of the component (meaning what props it accepts).
  • The process of building reusable components needs to be clear for all team members. I've seen it happen that someone needs a change to a component and adds very specific business logic to an otherwise reusable component. This decreases reusability of the component.
    • The better solution is to simply create a new "specialized" component that builds on top of the reusable component, this is the strength of React and trivial to do.
    • Duplicating the component code is also an option, but not really preferred since you will have to make changes in multiple places, which can become hard to maintain.
  • I found that developing components with Storybook helps a lot, because (1) you work on the component in isolation from the app, which forces you to think about the component API more and (2) you are creating "living documentation" (changes to the code are immediately visible) that is easy to find for all team members.
  • There is a fine line between a good reusable component and something that is over-engineered and too complex to use. You can only decide yourself (based on your use case) how much you need to be able to customize your component through props. Again, often it is better to create a new component instead of adding a new feature.
    • Do you need to support custom styling through className or do you need completely headless components? Should dark mode be built in?
  • What I've learned is that if you have two options for an implementation, you should pick the one that allows you to easily make changes in the future.
    • If you are building a button that has a "danger" state, you could add an <Button isDanger={true}> prop, but it might be better to do <Button type="danger"> if you foresee multiple types being added in the future (also, having the props <Button isDanger isWarning /> does not make sense).
  • However, you should also realize that we cannot predict the future. Adding a feature "because we might need it next sprint" is a bad idea, because requirements change all the time and it leads to over-engineering.

Like I said, building reusable component is not an easy task and it takes some practise, but leads to better code quality down the line.

Collapse
 
codewithshahan profile image
Programming with Shahan

Great explanation. Its really helpful.

Collapse
 
brense profile image
Rense Bakker

Yes! Great article on writing good reusable low level components. I just want to point out that you may not want to apply these same rules to all components in a project when they are higher in the tree because of excessive prop drilling (passing down props down the entire component tree). In terms of layering I think it should be something like this: app/root -> composable layout -> (... additional app structure, all composable) -> business logic (actions/fetch/side effects) -> reusable leafs.

Collapse
 
codewithshahan profile image
Programming with Shahan

I agree.

Collapse
 
kmsaifullah profile image
Khaled Md Saifullah

Nice demonstration. Thanks for sharing this.

Collapse
 
codewithshahan profile image
Programming with Shahan

Thanks for your feedback.

Collapse
 
sakeenanavi profile image
Sakeena Navavi

great article!

Collapse
 
behan05 profile image
Behan kumar

Great article! , i hope this article will help to all beginners including me.

Collapse
 
lotfijb profile image
Lotfi Jebali

thanks for sharing

Collapse
 
alexanderop profile image
Alexander Opalic

good article the same applies to otoher frameworks like vue, angular.

Collapse
 
codewithshahan profile image
Programming with Shahan

yes.

Collapse
 
uttam_py profile image
Uttam Sharma • Edited

Well explained thanks for sharing

Collapse
 
codewithshahan profile image
Programming with Shahan

Glad it helped.

Collapse
 
tmnrp profile image
TonyMartin • Edited

Scenario: You have a reusable component say "ButtonSave" which take props be label or styles or even icon.
A situation where this button is used in 100 different pages, and now your boss tells you a change(add/remove/etc) on the button.
You have to apply those changes in 100 location.

What would be the best practices on that ?
// maybe create dedicated wrappers for each usecase?

Collapse
 
zerokoul profile image
Jose

Γ“timo artigo :)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.