An Introduction to Reusable Components and how to create Typography Component
Tags: webdev, javascript, react, tutorial
Published: 2020-09-08 15:41:25

One reason why React has become so popular is because of its reusable components. During the last few years, the concept of design system has also become popular among web developers.

I see many people making a mistake when learning React: they go straight to learning Redux and start building complex applications. They forget about learning the basics. 

Because of that, they don't know why they are using React. Is it really useful or is it just trendy?

In this article, we will look at what reusable components are, and why they are one of the best ways to get started with React. And we will build a typography component together.

## What are reusable components?

When a component is used more than once, it is reusable. For example in a list, we don't want to make a list item more than once. So we have to make that component reusable.

But reusable components are more than just an item inside a list. Some examples of reusable components are button and input. These are global components as they can be used anywhere.

Some are reusable but it's not necessary that they can be used everywhere. One example is that `<tr>, <th>, <td>` are reusable in `<table>` but cannot (should not) be used anywhere else.

You might already use reusable components. For example, if you are using BEM naming, you can see that Block names are global components, and Element names are scoped components.

Reusable components get more exciting when it comes to React.

## Why you should care about them

At this point, you might already see the benefits of reusable components. But here is a list:

### Efficient

You no longer have to spend your time thinking about pixels and doing the same things over and over again. You can save time by relying on your reusable components. This means that you have more time to improve quality, get your app done faster, and reduce costs.

### Consistent

Having consistency in your application is more important than you might think. As your users start to use your application, they will start to learn about it. They will start to find a pattern. 

An application with consistency will help your users find information faster and with less confusion.

### Maintainable

Let's say that your designers decide to change the padding in the buttons. Now you have to search for every place that has `<button>`, go to every CSS file, and try to find where the padding is.

That is a lot of work. So instead of doing that, if you have reusable components you just need to change it in one place.

### Avoids duplicated code

Duplicated code is not a bad thing, as it makes your app more flexible. But the code that you have to write again more than three times is not a good thing. Using reusable components helps you avoid copying your code every time.

## How to make a good reusable component

Building a reusable component can be tricky. Here are a few things you want to look out for:

### Component should be dumb

For example, the Button should not know the current theme. Instead, the application should tell the Button which theme it is.

#### Incorrect
```jsx
const Button = ({ children, ...props }) => {
  const { state } = useContext(AppContext);
  return (
    <button
      className={cn({
        "button--theme-dark": state.theme === "dark",
      })}
      {...props}
    >
      {children}
    </button>
  );
};
```

In this example, we get the global state from `AppContext` in `Button` component. This means we have created a dependency between the `Button` and the `Application`. Therefore, the component is only reusable in the Application Context and we want to avoid this.

#### Correct
```jsx
const Button = ({  theme, children, ...props }) => {
  return (
    <button
      className={cn({
        "button--theme-dark": theme === 'dark',
      })}
      {...props}
    >
      {children}
    </button>
  );
};
```
The button in this example is independent and can be used in any application. This is what we want to do.

### Scalable

The component should be flexible enough that you can add more configuration easily later on. 

In this example, instead of having `hasPrimaryColor` as a boolean, it should have a prop: `backgroundColor="primary"`. Some other props should not be `boolean` like: `size`, `varient`,...

### Incorrect

```jsx
const Button = ({ hasPrimaryColor, children, ...props }) => {
  return (
    <button
      className={cn({
        "button--color-primary": hasPrimaryColor,
      })}
      {...props}
    >
      {children}
    </button>
  );
};

```

Correct

```jsx
const Button = ({ color, children, ...props }) => {
  return (
    <button
      className={cn({
        "button--color-primary": color === "primary",
      })}
      {...props}
    >
      {children}
    </button>
  );
};
```

### Simple

The more complex the component is, the harder to maintain it. You might hear the terms: Stateless Components and Stateful Components, most of the time Stateless Components are simpler than Stateful Components. 

But what are the differences? Well.. it deserves a separate post. But basically, if you can move the logic outside the component and keep it dumb, then you should do it 🙂

## Building a Typography Component

### User stories

- As a user, I can choose to have 10 variants: h1, h2, h3, h4, h5, h6, subheading 1, subheading 2, body 1, and body 2
- As a user, I can choose to have primary or error colors

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/c7hsc76fr0s05agdohpp.png)

[Design On Figma](https://www.figma.com/file/mDcHD5OOm3kz82hpIWFsjE/Typography-Example)

#### Step 1: Create-react-app and install classnames

Let's create a React app and install [classnames](https://www.npmjs.com/package/classnames). `Classnames` will allow you to have multiple classes conditionally.

```command
npx create-react-app typography
cd typography
npm i classnames

```

#### Step 2: Import font

You can go to [Google Font](https://fonts.google.com/) to choose the ones you wish. In our case, we use [Ubuntu](https://fonts.google.com/specimen/Ubuntu?query=Ubuntu).

You can import by using `<link>` tag inside `<head>`

```html
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&family=Ubuntu+Mono&display=swap" rel="stylesheet">
```
or you can import in your css file

```css
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&family=Ubuntu+Mono&display=swap');
```

#### Step 3: Use the font and reset the default styling

Let's reset some of the default styles and use our font. By resetting the default, we are free to give it our own style without knowing what the default values are.

In our cause, let's remove the default padding and margin. Some other components might have `border`, `background-color`, `text-decoration`,..

```css
body {
  margin: 0;
  font-family: "Poppins", sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

*,
*::after,
*::before {
  box-sizing: inherit;
}

h1,
h2,
h3,
h4,
h5,
h6,
p {
  margin: 0;
  padding: 0;
}
```

#### Step 4: Create a typography component
Always remember to pass `...props` to your component, so that we don't lose the [default attribute](https://www.w3schools.com/tags/ref_attributes.asp).

```jsx
import React from "react";
import cn from "classnames";
import "./typography.css";

// Defining the HTML tag that the component will support
const variantsMapping = {
  h1: "h1",
  h2: "h2",
  h3: "h3",
  h4: "h4",
  h5: "h5",
  h6: "h6",
  subheading1: "h6",
  subheading2: "h6",
  body1: "p",
  body2: "p",
};

// Create a functional component that take 
// variant: the selected html tag
// color: the selected color
// children: the node passed inside the Component
// ...props: the default attribute of the Component
const Typography = ({ variant, color, children, ...props }) => {
// If the variant exists in variantsMapping, we use it. 
// Otherwise, use p tag instead.
  const Component = variant ? variantsMapping[variant] : "p";

  return (
    <Component
      className={cn({
        [`typography--variant-${variant}`]: variant,
        [`typography--color-${color}`]: color,
      })}
      {...props}
    >
      {children}
    </Component>
  );
};

export default Typography;

```
#### Step 5: Add styling to your component

The last step is to give style to our component. This code is straightforward, we add different `font-size` and `font-weight` to each variant option and `color` to color option.

```css
.typography--variant-h1 {
  font-size: 6rem;
  font-weight: 500;
}

.typography--variant-h2 {
  font-size: 3.75rem;
  font-weight: 500;
}

.typography--variant-h3 {
  font-size: 3rem;
  font-weight: 500;
}

.typography--variant-h4 {
  font-size: 2.125rem;
  font-weight: 500;
}

.typography--variant-h5 {
  font-size: 1.5rem;
  font-weight: 500;
}

.typography--variant-h6 {
  font-size: 1.25rem;
  font-weight: 500;
}

.typography--variant-subheading1 {
  font-size: 1rem;
  font-weight: 500;
}

.typography--variant-subheading2 {
  font-size: 0.875rem;
  font-weight: 500;
}

.typography--variant-body1 {
  font-size: 1rem;
}

.typography--variant-body1 {
  font-size: 0.875rem;
}

.typography--color-primary {
  color: #f2994a;
}

.typography--color-error {
  color: #eb5757;
}


```

#### Challenge

The component is not totally complete. I challenge you to add more props like: `align`, `display`, `marginButton`,...

#### Result
You can find the source code [here](https://github.com/nghiemthu/typography-article) if you want to check it out.


## Conclusion

After making Typography component, we can see that making reusable components can be difficult and usually saves you a lot of time in the future. It is also a good way to get started learning React or Vue.

Next time, when working with React, don't be lazy and simply copy code from other places. If you think it should be a component, make it one. It will help you out a lot.

Here are 2 challenges to get started creating Reusable components and learning React:

[Button Component](https://devchallenges.io/challenges/ohgVTyJCbm5OZyTB2gNY)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/7jbh6o3xqvfd4adz8irj.png)

[Input Component](https://devchallenges.io/challenges/TSqutYM4c5WtluM7QzGp)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hr2e7a2e7wiue1oebidi.png)

Do you have some questions? Free feel to leave me a comment 😁

🐦 [Thu Nghiem Twitter](https://twitter.com/thunghiemdinh)
🐦 [Devchallenge.io Twitter](https://twitter.com/devchallengesio)
🔥 [Devchallenges Website](https://devchallenges.io/)