DEV Community

TEREN VELAN
TEREN VELAN

Posted on

Creating Variants of React Component

So recently i've been working on building react components with StorybookJS. For those who don't know what Storybook is ,Storybook is a JavaScript tool that allows developers to create organised UI systems making both the building process and documentation more efficient and easier to use.

With that said , i would like to share a common design pattern that i learned from building these components, and that is creating variants of a single component through the use of props.

*Do note that i'm using ChakraUI as my UI library

In this example we take a look at building a simple button component. Starting with the Button function

export function ReactButton(props) {
  let {children} = props;

  return (
    <Button>
      {children}
    </Button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Following that , we add propTypes to establish our variants, you can definitely do without this step but it helps to ensure we're using the right data type

ReactButton.propTypes = {
  variant: PropTypes.arrayOf(
  PropTypes.oneOf(["primary,secondary,outline,danger"])
  ),
};

export function ReactButton(props) {
  let {children} = props;

  return (
    <Button>
      {children}
    </Button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now for the game changer, we make use of CSS to enable to the browser to display what we want want it to display. Using a switch statement we set the base styles for different variants

 let baseStyles = {},
    invertStyles = {},
    stretchedStyles = {};

switch (variant) {
    case "primary":
      baseStyles = {
        backgroundColor: "brand1.primary.400",
        color: "#fff",
        _hover: {
          backgroundColor: "brand1.primary.500",
        },
      };
     break;
    case "secondary":
      baseStyles = {
        backgroundColor: "brand1.secondary.400",
        color: "#fff",
        _hover: {
          backgroundColor: "brand1.secondary.500",
        },
      };
     break;
    case "outline":
      baseStyles = {
        backgroundColor: "transparent",
        color: "brand1.secondary.300",
        borderColor: "brand1.secondary.300",
        _hover: {
          color: "brand1.secondary.500",
          borderColor: "brand1.secondary.500",
        },
      };
     break;
    case "danger":
      baseStyles = {
        backgroundColor: "brand1.danger.400",
        color: "#fff",
        _hover: {
          backgroundColor: "brand1.danger.500",
        },
      }
      break;

Enter fullscreen mode Exit fullscreen mode

You will notice i created an empty object for inverted and stretched, keep reading on to see how we implement those.

With that we set our default variant to primary, and establish some props like invert and stretched and give them a boolean value

let {
    children,
    variant = "primary",
    invert = false,
    stretched = false,
  } = props;
Enter fullscreen mode Exit fullscreen mode

Now we can go ahead and add stylings for our props that we declared (invert and stretched).

switch (variant) {
    case "primary":
      baseStyles = {
        backgroundColor: "brand1.primary.400",
        color: "#fff",
        _hover: {
          backgroundColor: "brand1.primary.500",
        },
      };
      if (invert) {
        invertStyles = {
          backgroundColor: "brand1.primary.50",
          border: "0.25rem solid #ffe8ae",
          color: "#969696",
          _hover: {
            backgroundColor: "brand1.primary.100",
          },
        };
      }
      if (stretched) {
        stretchedStyles = {
          width: "100%",
          justifyContent: "space-between",
        };
      }
      break;

    case "secondary":
      baseStyles = {
        backgroundColor: "brand1.secondary.400",
        color: "#fff",
        _hover: {
          backgroundColor: "brand1.secondary.500",
        },
      };
      if (invert) {
        invertStyles = {
          backgroundColor: "brand1.secondary.50",
          border: "0.25rem solid #c4c9ef",
          color: "#969696",
          _hover: {
            backgroundColor: "brand1.secondary.100",
            color: "#fff",
          },
        };
      }
      if (stretched) {
        stretchedStyles = {
          width: "100%",
          justifyContent: "space-between",
        };
      }
      break;

    case "outline":
      baseStyles = {
        backgroundColor: "transparent",
        color: "brand1.secondary.300",
        borderColor: "brand1.secondary.300",
        _hover: {
          color: "brand1.secondary.500",
          borderColor: "brand1.secondary.500",
        },
      };
      if (invert) {
        invertStyles = {
          color: "#c9c9c9",
          borderColor: "#c9c9c9",
          _hover: {
            color: "#fff",
            borderColor: "#fff",
          },
        };
      }
      if (stretched) {
        stretchedStyles = {
          width: "100%",
          justifyContent: "space-between",
        };
      }
      break;

    case "danger":
      baseStyles = {
        backgroundColor: "brand1.danger.400",
        color: "#fff",
        _hover: {
          backgroundColor: "brand1.danger.500",
        },
      };
      if (invert) {
        invertStyles = {
          backgroundColor: "brand1.danger.50",
          border: "0.25rem solid #feb6b9",
          color: "#969696",
          _hover: {
            backgroundColor: "brand1.danger.100",
            color: "#fff",
          },
        };
      }
      if (stretched) {
        stretchedStyles = {
          width: "100%",
          justifyContent: "space-between",
        };
      }

      break;
Enter fullscreen mode Exit fullscreen mode

Now that we have our complete styles in place and in a conditional statement , let us look at how we can use this styles in our component.

By spreading our stylings into the component through the object spread operator. Take note on the order in which the styles are added.

  return (
    <Button
      {...baseStyles}
      {...props}
      {...invertStyles}
      {...stretchedStyles}
    >
      {children}
    </Button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now about the order in which we have placed our props , baseStyles first followed by props then invertStyles and lastly stretchedStyles. The order is very important as the latter props on the list will take precedence over the styles of the earlier props on the list. Thats why we have baseStyles first , this give the developer the flexibility to deviate from the baseStyles.

To demonstrate, i will show 4 different buttons using the variant="danger" and the invert and stretched props

<Button variant="danger">Danger Button</Button>
Enter fullscreen mode Exit fullscreen mode

Screenshot 2021-09-26 at 11.51.50 PM

<Button variant="danger" stretched>Danger Button</Button>
Enter fullscreen mode Exit fullscreen mode

Screenshot 2021-09-26 at 11.52.48 PM

<Button variant="danger" invert>Danger Button</Button>
Enter fullscreen mode Exit fullscreen mode

Screenshot 2021-09-26 at 11.53.45 PM

<Button variant="danger" invert stretched>Danger Button</Button>
Enter fullscreen mode Exit fullscreen mode

Screenshot 2021-09-26 at 11.54.29 PM

Thats all folks , i hope this helps you as much as it has helped me. If theres any way i can improve on this i would really love to hear it. Drop a comment below.

Top comments (0)