How to implement dynamic button styling using tailwindcss

Viewed 40

What I Want:

I have a Button component, which accepts a variant prop. I want the className for the button to change based on the prop passed to it.

The goal is to avoid using if/else statements buttons, instead using a single button that dynamically changes based on the need.

Things to Note:

  • I'm using Tailwindcss as the primary styling engine.

Ideal Solution:

// provide the variant to component:
<Button variant="default" />

// would return:
<button className={variantClasses.default} />

Button Component:

interface Props {
  variant: string;
  innerText: string;
}

function Button({ variant, innerText }: Props) {
  // classes for all variants
  const variantClasses = {
    error: "...classes",
    default: "...classes",
  };

  // if no variant provided, return an error variant.
  if (!variant) {
    return <button className={variantClasses.error}>Error</button>;
  }
  // else return the correct variant with matching styles.
  else {
    return (
      <button className={`${variantClasses}.${variant}`}>
        {innerText || "Button"}
      </button>
    );
  }
}

export default Button;

What I've Tried:

  • Changing the interpolation values inside button className.
  • Using an array for variantClasses instead of an object.
1 Answers

Solution:

Tailwind does not support interpolation through classes the same way other css solutions do, so a workaround was needed. We can do this instead:

Here's what to do

  1. Define array of variants, providing name and className attributes.
  2. Search the array for an entry where name matches variant provided.
  3. If nothing matches, throw error... else create a new variable containing matching classes.
  4. Provide new variable to className prop on button component.
interface Props {
  variant: string;
  innerText: string;
}

function Button({ variant, innerText }: Props) {
  const variantClasses = [
    {
      name: "error",
      className:
        "...classes",
    },
    {
      name: "filled",
      className:
        "...classes",
    },
  ];
  let activeClass = variantClasses.find((v) => v.name === variant);

  // if matching variant is not found, throw error variant
  if (!activeClass) {
    return <button className={variantClasses[0].className}>Error</button>;
  }
  // if variant found, return matching class
  else {
    return (
      <button className={activeClass?.className}>
        {innerText || "Button"}
      </button>
    );
  }
}

export default Button;
Related