DEV Community

Islam Naasani
Islam Naasani

Posted on • Originally published at linkedin.com

Using cva (Class Variance Authority) to scale TailwindCSS

While TailwindCSS is a great styling solution, it has a scalability issue and it may quickly go out of hand when writing huge components with a lot of different states. Even when using clsx utility it's still hard to maintain and extend, and this is when cva (Class Variance Authority) library shines.

If you are using shadcn/ui—which is a fantastic component library, by the way—then you're already using cva, shadcn/ui uses it for managing different component states and configurations, for example, the button component has multiple variants (default, secondary, outline...), if you try to implement it by doing conditions manually, the code will quickly start to look ugly and hard to extend, but with cva, it's quite simple!

let's try to write the same component with those three ways: no-libraries, with clsx, and with cva.
No-libraries:

export const Button = ({ variant = "default", className, ...props }) => {
  return (
    <button
      {...props}
      className={`inline-flex items-center justify-center ${
        variant === "default" ? " bg-primary text-primary-foreground hover:bg-primary/90" : ""
      }${
        variant === "secondary"
          ? "bg-secondary text-secondary-foreground hover:bg-secondary/80"
          : ""
        // ...
      } ${className}`}
      // 🟥 Hard to maintain and looks ugly.
    />
  );
};
Enter fullscreen mode Exit fullscreen mode

clsx:

export const Button = ({ variant = "default", className, ...props }) => {
  return (
    <button
      {...props}
      className={clsx(
        "inline-flex items-center justify-center",
        variant === "default" && " bg-primary text-primary-foreground hover:bg-primary/90",
        variant === "secondary" && "bg-secondary text-secondary-foreground hover:bg-secondary/80",
        // ...
        className
      )}
      // 🟧 More readable but still not very scalable.
    />
  );
};
Enter fullscreen mode Exit fullscreen mode

And now with cva!:

const buttonVariants = cva("inline-flex items-center justify-center", {
  variants: {
    variant: {
      default: "bg-primary text-primary-foreground hover:bg-primary/90",
      secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
    },
    size: {
      default: "h-10 px-4 py-2",
      sm: "h-9 rounded-md px-3",
      lg: "h-11 rounded-md px-8",
    },
  },
  defaultVariants: {
    variant: "default",
    size: "default",
  },
  // 🟩 Readable and pretty easy to maintain and scale.
});
export const Button = ({ variant, size, className, ...props }) => {
  return <button {...props} className={buttonVariants({ variant, size, className })} />;
};
Enter fullscreen mode Exit fullscreen mode

Reference

Top comments (0)