DEV Community

Cover image for An Introduction to Reusable Components and how to create Typography Component
Thu Nghiem
Thu Nghiem

Posted on

An Introduction to Reusable Components and how to create Typography Component

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

const Button = ({ children, ...props }) => {
  const { state } = useContext(AppContext);
  return (
    <button
      className={cn({
        "button--theme-dark": state.theme === "dark",
      })}
      {...props}
    >
      {children}
    </button>
  );
};
Enter fullscreen mode Exit fullscreen mode

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

const Button = ({  theme, children, ...props }) => {
  return (
    <button
      className={cn({
        "button--theme-dark": theme === 'dark',
      })}
      {...props}
    >
      {children}
    </button>
  );
};
Enter fullscreen mode Exit fullscreen mode

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

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

Enter fullscreen mode Exit fullscreen mode

Correct

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

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

Design On Figma

Step 1: Create-react-app and install classnames

Let's create a React app and install classnames. Classnames will allow you to have multiple classes conditionally.

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

Enter fullscreen mode Exit fullscreen mode

Step 2: Import font

You can go to Google Font to choose the ones you wish. In our case, we use Ubuntu.

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

<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&family=Ubuntu+Mono&display=swap" rel="stylesheet">
Enter fullscreen mode Exit fullscreen mode

or you can import in your css file

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

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,..

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;
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Create a typography component

Always remember to pass ...props to your component, so that we don't lose the default attribute.

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;

Enter fullscreen mode Exit fullscreen mode

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.

.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;
}


Enter fullscreen mode Exit fullscreen mode

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 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

Alt Text

Input Component
Alt Text

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

🐦 Thu Nghiem Twitter
🐦 Devchallenge.io Twitter
🔥 Devchallenges Website

Latest comments (7)

Collapse
 
sergey98am profile image
Sergey Gabrielyan

Hello 👋. In my project I have primary and secondary buttons with global .btn class which I use on a and button tags, and I can easily change .btn class padding if the designer wishes, but I want to use Button component, because lots of project examples use that option. If not difficult, can you give other examples why to use Button component, what are the benefits?

Collapse
 
dalalrohit profile image
Rohit Dalal

Great read, thank you ;)

Collapse
 
devworkssimone profile image
DevWorksSimone

Nice, will try the challenges. Noob question... variant?variantsMapping[variant]:"p"; written this way will always output variantsMapping wheter it is in the mapping or not? I mean variant could be' any truthy value

Collapse
 
nghiemthu profile image
Thu Nghiem

Hi sorry for the late reply. variant ? variantsMapping[variant] : "p" means that

if (variant) {
return variantsMapping[variant];
} else {
return "p";
}
Collapse
 
devworkssimone profile image
DevWorksSimone

Ok bit even "bingobongo" is truthy that would lead to undefined or error doesnt it?

Thread Thread
 
nghiemthu profile image
Thu Nghiem

yeah good points. it will return undefined and error in that case :) good catch. usually, it will have propTypes or typescript to prevent those. but yeah we can add more check to this. But this is an example and the component is not fully complete.

Collapse
 
zhabinsky profile image
Vlad Zhabinsky

nice