DEV Community

Jemal Ahmedov
Jemal Ahmedov

Posted on

React Extension components

Overview

Reusing React components can be tricky. You need to think twice before you try to extend a reusable component, otherwise, it might become a massive beast which can no longer be tamed. Talking from experience, you need to come up with a plan about how to extend your reusable components before you start playing with files long thousands of lines of code, which once used to be a simple Button component. This is when the idea for React extensions was born.

The idea of having extensions is to be able to build different components which can reuse each other. It was inspired by the inheritance mechanism, which you can apply on a class upon another class to retain similar implementation. This way you will have small components built with single responsibility in mind which will be easy to scale up and maintain later on.

Building React Extensions

For this example, I will build a reusable Button component which I will extend later on with additional features.

Let's start with a simple Button:

// Different button variants which will apply different styling
const btnVariants = {
  primary: "primary",
  danger: "danger",
  tertiary: "tertiary",
};

// Variant styles
const variantStyles = {
  [btnVariants.primary]: {
    background: "#1890ff",
  },
  [btnVariants.danger]: {
    background: "#f81d22",
  },
  [btnVariants.tertiary]: {
    background: "#fff",
    color: "#000",
  },
};

function BaseButton({ variant, ...props }) {
  const btnStyle = {
    color: "#fff",
    border: "none",
    padding: "5px 10px",
    ...variantStyles[variant],
  };

  return <button {...props} style={btnStyle} />;
}
Enter fullscreen mode Exit fullscreen mode

Button component which has a variant prop to apply different styling. You can think of this component as the Base component from which each extension will inherit from. This component will have the functionality that every component is supposed to have and nothing else. It's very important to correctly decouple the different functionalities to build a maintainable component.

Let's build an extension which will easily allow adding an icon to the BaseButton. To do this, let's create a new React component which will reuse the BaseButton and build on top of it.

const container = {
  display: "flex",
  alignItems: "center",
};

function IconButton({ icon = "", iconPosition = "left", ...props }) {
  const iconStyle = {
    width: 20,
    height: 20,
    ...(iconPosition === "left" ? { marginRight: 10 } : { marginLeft: 10 }),
  };

  const Icon = <img src={icon} style={iconStyle} />;

  return (
    <Button {...props}>
      <div style={container}>
        {iconPosition === "left" && Icon}
        {props.children}
        {iconPosition === "right" && Icon}
      </div>
    </Button>
  );
}
Enter fullscreen mode Exit fullscreen mode

The IconButton component applies two new props - icon and iconPosition which are relevant to this new component. The BaseButton doesn't need to know about them. This component is reusing the BaseButton without neglecting any of its props but building on top of.

You can assign the extensions to the base component like you are assigning a property to an object. This will allow to easily access any of the extensions while using the component.

const Button = BaseButton;
Button.WithIcon = IconButton;

<Button variant="primary">My Button</Button>
<Button.WithIcon variant="tertiary" icon="random-path/icon.jpg">My Button with Icon</Button.WithIcon>
Enter fullscreen mode Exit fullscreen mode

This pattern allows building truly reusable components without the worry that they will grow to a point where we won't be able to maintain them. I hope this will find out well and will help you while working with React. Let me know what you think about this pattern.

Top comments (0)