Reusable Component in React - Is It Really Shareable?
Reusable Component in React - Is It Really Shareable?

A lot of students ask me what are reusable components, how to know that we need to make something reusable and is it really a good idea to always try and implement reusable components inside your frontend framework.

The first and really important point is to think about your components as reusable functions.

We can apply to the functions the same rules as for components.

Just imagine that we implement some feature and it looks really big. At least your file is huge (maybe 200 lines or more). At this moment you typically thing that you must split it in several components.

Then you have some questions.

Questions

Should it be reusable?

The first question is do we need to make it reusable at all? Maybe not. But your component is big and it is difficult to read it. What you can do?

You can move part of your component out and simply split in 2 different components

Just imagine that we implement the feature which is called register page.

/register
  /components
    Main.jsx

This is why we have a folder register with components folder and a single Main component which is extremely big. So we want to split it. We can just take a part of it and move it. We don't need to make it sharable and we just put it near.

/register
  /components
    Main.jsx
    Form.jsx

So we moved our form our of register component. We didn't do it reusable at all. We simply moved it out as easy as possible.

I wrote example like we are talking about React but all these rules can also be applied to Vue, Angular or any other framework.

The main idea is not to make something reusable but just to split your huge component which makes it easier to support. All these components are still living in register folder. They are completely isolated inside our register feature and they are not reusable. This is why you should not think a lot about how to move them out.

Make it shareable between features

At some point you might implement login page

/login
  /components
    Main.jsx
    Form.jsx

Here you also started just with Main file, it became huge and you moved a form out of Main. But it is still not reusable. This is totally fine, your code is easier to support.

At that specific moment you might think "But I have similar form in register and login pages. I must for sure share them". Is it always a good idea? It depends.

From my experience in a lot of cases it is much easier to copy paste code than to share it. It is easier to change and it is faster to ship.

If you try to make something shareable which is not really shareable you always have problems that you write if conditions and you check that if you are inside register page then you do this and inside login are do that.

Component with conditions

This is a bad approach and a sign that your component is not really shareable.

Your shareable component should never have cases with specific implementation.

Article form

For example this form that you see here is a fully configurable form. Something similar you can build between register and login.

So for this case we want to change our structure.

/register
  /components
    Main.jsx
/login
  /components
    Main.jsx
/shared
  /components
    Form.jsx

Now we moved it to shared folder which allowed us to remove it from both features. Most importantly it must be configurable and it should not have any logic related to either register or login.

Real example - loading

Now I want to show you some real examples of shareable components so you understand better when they are really shareable. All examples that I want to show you are coming from my Angular + NgRx course.

Shared components

Here we have lots of different reusable components. These are Angular components but the same rules you can apply to any component.

First of all let's look on something simple. We have a loading component which is extremely simple.

// src/app/shared/components/loading/loading.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'mc-loading',
  template: '<div>Loading...</div>',
  standalone: true,
})
export class LoadingComponent {}

It doesn't have any logic but just some markup which allows us to reuse loading component at any place of our application.

Real example - error message

We can have something similar (where we just render some markup) but with possibility to configure it through inputs.

// src/app/shared/components/errorMessage/errorMessage.component.ts
import { Component, Input } from '@angular/core';

@Component({
  selector: 'mc-error-message',
  template: '<div>{{message}}</div>',
  standalone: true,
})
export class ErrorMessageComponent {
  @Input() message: string = 'Something went wrong';
}

Here we have an errorMessage component which we can configure with the inputs.

Real example - pagination

We can also have a shareable component with some business logic inside. For example pagination component can be reused for different pages. Let's say you are rendering a list of users and on the other page a list of articles.

// src/app/shared/components/pagination/pagination.component.html
<ul class="pagination">
  <li
    *ngFor="let page of pages"
    class="page-item"
    [ngClass]="{ active: currentPage === page }"
  >
    <a [routerLink]="[url]" [queryParams]="{ page: page }" class="page-link">
      {{ page }}
    </a>
  </li>
</ul>

And some logic

// src/app/shared/components/pagination/pagination.component.ts

import { CommonModule } from '@angular/common';
import { Component, Input, OnInit } from '@angular/core';
import { RouterLink } from '@angular/router';
import { UtilsService } from 'src/app/shared/services/utils.service';

@Component({
  selector: 'mc-pagination',
  templateUrl: './pagination.component.html',
  standalone: true,
  imports: [RouterLink, CommonModule],
})
export class PaginationComponent implements OnInit {
  @Input() total: number = 0;
  @Input() limit: number = 20;
  @Input() currentPage: number = 1;
  @Input() url: string = '';

  pagesCount: number = 1;
  pages: number[] = [];

  constructor(private utilsService: UtilsService) {}

  ngOnInit(): void {
    this.pagesCount = Math.ceil(this.total / this.limit);
    this.pages =
      this.pagesCount > 0 ? this.utilsService.range(1, this.pagesCount) : [];
  }
}

So can configure our pagination and inside we have some business logic to create a list of pages.

Real example - popular tags

Another shareable component is a full blown feature. It has not only markup and business logic inside but also a state, working with API and much more. In our case popularTags is such component.

Popular tags

As you can see we have not only a component but also a service, store of NgRx and some internal types. We can render popularTags in any place of our application and it will fetch needed data from API, store them in state and render inside.

And the most important point to remember is that it is much more difficult to make something shareable and reusable. This is why copy paste is a winner in a lot of cases.

And actually if you want to improve your Javascript knowledge and prepare for the interview I highly recommend you to check my course Javascript Interview Questions.