Skip to content
Created: April 18, 2025

1. What is CVA?Understand in one sentence ​

CVA = Use "configuration" to describe class combination rules, and use "parameters" to generate className

It solves only one problem:

"Conditional Combination Explosion" of Component Styles

2. What would happen without a CVA?(pain point) ​

❌ Traditional writing method (unmaintainable) ​

tsx
<button
  className={`
    px-4 py-2 rounded
    ${variant === "primary" && "bg-blue-600 text-white"}
    ${variant === "outline" && "border"}
    ${size === "lg" && "h-12 text-lg"}
    ${disabled && "opacity-50"}
  `}
>

Question:

  • Conditional dispersion
  • No type hints
  • Add a variant → rewrite a bunch of if

3. CVA’s core API ​

ts
import { cva } from "class-variance-authority"

minimal model ​

ts
const buttonVariants = cva(
  "base-class",
  {
    variants: {
      variant: {
        primary: "...",
        outline: "...",
      },
      size: {
        sm: "...",
        lg: "...",
      },
    },
  }
)

Use:

ts
buttonVariants({ variant: "primary", size: "lg" })

4. Complete Button example (key points) ​

ts
const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md font-medium",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground",
        outline: "border border-input",
        ghost: "hover:bg-accent",
      },
      size: {
        sm: "h-9 px-3",
        md: "h-10 px-4",
        lg: "h-11 px-8",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "md",
    },
  }
)

Use:

tsx
<Button variant="outline" size="lg" />

5. Variants / defaultVariants essence ​

What are variants? ​

ts
variants: {
  size: {
    sm: "h-9",
    lg: "h-11",
  }
}

👉 key is parameter, value is class

What are defaultVariants? ​

ts
defaultVariants: {
  size: "md"
}

👉 Default value when no parameters are passed

6. Compound conditions: compoundVariants (very important) ​

Append class when multiple variants satisfy at the same time

ts
compoundVariants: [
  {
    variant: "outline",
    size: "lg",
    class: "uppercase"
  }
]

Equivalent to:

ts
if (variant === "outline" && size === "lg") {
  class += " uppercase"
}

7. Boolean variant (commonly used) ​

ts
variants: {
  disabled: {
    true: "opacity-50 pointer-events-none",
    false: ""
  }
}

Use:

tsx
<Button disabled />

8. CVA + TypeScript (core advantages) ​

Automatically derive Props types ​

ts
import { VariantProps } from "class-variance-authority"

type ButtonVariants = VariantProps<typeof buttonVariants>

interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    ButtonVariants {}

Effect:

tsx
<Button variant="xxx" /> ❌ TS 报错
<Button size="lg" /> ✅

9. className merge (required test) ​

tsx
<button
  className={cn(buttonVariants({ variant, size }), className)}
/>

Users can:

tsx
<Button className="w-full" />

10. Design ideas of CVA (very important) ​

1️⃣ Configure the driver, not the logical driver ​

❌ if / else ✅ declarative config

2️⃣ Style = part of the design system ​

ts
variant="destructive"

Not:

ts
bg-red-500

3️⃣ The style is "Finite State Machine" ​

  • variant
  • size
  • state

11. When should CVA be used? ​

✅ Used in:

  • Button
  • Badge
  • Alert
  • Input
  • Tabs Trigger

❌ Not required in:

  • Page level layout
  • Strong business coupling components
  • disposable components

12. Practical combat: write a Badge yourself (step by step) ​

ts
const badgeVariants = cva(
  "inline-flex items-center rounded-full px-2 py-1 text-xs",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground",
        success: "bg-green-500 text-white",
        warning: "bg-yellow-500 text-black",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
)
tsx
function Badge({ variant }: VariantProps<typeof badgeVariants>) {
  return <span className={badgeVariants({ variant })} />
}

13. Common Misunderstandings ​

  • ❌ Write dozens of utilities in variant
  • ❌ Treat business status (loading) as variant
  • ❌ Add business judgment to UI components

14. Are you considered “good at CVA”? ​

You can:

  • Write out CVA configuration without reading the documentation
  • Correctly split variant / size / state
  • Use compoundVariants
  • Constrain component APIs with TS types