Improving the Button Component in React

2023/06/09
This article was written by an AI 🤖. The original article can be found here. If you want to learn more about how this works, check out our repo.

Buttons are an essential part of any design system, and creating them requires careful consideration to ensure they are both easy to use and flexible enough to fit various use cases. In this article, we will explore some tips and tricks to improve the Button component in React.

To start, let's look at the basic Button component that we will build upon:

export function Button(props: React.ComponentProps<"button">) {
  return (
    <button {...props}>
      {props.children}
    </button>
  );
}

Assuming that the buttons are unstyled, which is the default for many CSS frameworks like Tailwind, we can use some sane defaults for the design. Placing the Button component on a page without any additional props or parameters should result in a button that fits in the design and is easily recognizable as a button.

A recognizable button tends to follow these design characteristics:

  • Horizontal padding is twice the vertical padding
  • High contrast colors are reserved for important buttons
  • Limit the number of buttons with eye-catching colors

We can achieve these defaults by adding some CSS to the Button component:

export function Button(props: React.ComponentProps<"button">) {
  return (
    <button
      {...props}
      className="px-4 py-2 text-white bg-blue-500 rounded hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-opacity-50"
    >
      {props.children}
    </button>
  );
}

This CSS uses Tailwind classes to set the horizontal padding, background color, and border radius. It also adds some hover and focus styles to make the button more interactive.

Another important consideration is the default type of the <button> element. By default, the type is "submit," which can cause issues if the button is not intended to submit a form. To avoid this, we can set the type to "button" by default:

export function Button(props: React.ComponentProps<"button">) {
  return (
    <button
      {...props}
      type={props.type ?? "button"}
      className="px-4 py-2 text-white bg-blue-500 rounded hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-opacity-50"
    >
      {props.children}
    </button>
  );
}

This code uses the nullish coalescing operator (??) to set the type to "button" if it is not already set.

Finally, we can add some additional props to make the Button component more flexible. For example, we can add a size prop to set the font size and padding:

export function Button(props: React.ComponentProps<"button"> & { size?: "sm" | "md" | "lg" }) {
  const { size = "md", ...rest } = props;
  const fontSize = size === "sm" ? "text-sm" : size === "lg" ? "text-lg" : "text-base";
  const padding = size === "sm" ? "px-2 py-1" : size === "lg" ? "px-6 py-4" : "px-4 py-2";
  return (
    <button
      {...rest}
      type={rest.type ?? "button"}
      className={`text-white bg-blue-500 rounded hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-opacity-50 ${fontSize} ${padding}`}
    >
      {rest.children}
    </button>
  );
}

This code adds a size prop that can be set to "sm," "md," or "lg" to adjust the font size and padding. It also uses some conditional logic to set the appropriate classes based on the size prop.

In conclusion, creating a Button component in React requires careful consideration of the design and flexibility. By using some sane defaults, setting the default type to "button," and adding some additional props, we can create a Button component that is both easy to use and flexible enough to fit various use cases.