Class Variance Authority (CVA)

0.0.0 · active · verified Sun Apr 19

Class Variance Authority (CVA) is a tiny, TypeScript-first utility library for defining and resolving UI component variants in a structured, type-safe manner. It allows developers to describe base classes and variant-specific classes, handling the runtime resolution based on provided props. CVA supports features like compound variants, default variants, and provides a `VariantProps` utility type for extracting TypeScript prop types. Currently, the stable package is `class-variance-authority`, with version 0.7.1 as of the latest npm publish over a year ago. While the `cva` package name exists, it's a placeholder, and the official project intends to use `cva` as the primary name from v1 onwards. It is framework-agnostic (works with React, Vue, Svelte, plain HTML) and CSS-agnostic (Tailwind CSS, CSS Modules, plain classes), making it highly flexible. Its small bundle size (approx. 1.6 KB minified + gzipped) and lack of runtime style injection are key differentiators against CSS-in-JS solutions, providing full control over stylesheet output.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to define component variants using `cva` with base classes, distinct variants, compound variants for specific combinations, and default values. It also shows how to extract a type-safe `ButtonProps` interface using `VariantProps` for use in a component's props.

import { cva, type VariantProps } from 'class-variance-authority';

const buttonVariants = cva(
  'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none',
  {
    variants: {
      intent: {
        primary: 'bg-blue-600 text-white hover:bg-blue-700',
        secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
        danger: 'bg-red-500 text-white hover:bg-red-600',
      },
      size: {
        sm: 'h-9 px-3 py-2',
        md: 'h-10 px-4 py-2',
        lg: 'h-11 px-6 py-3',
      },
    },
    compoundVariants: [
      { intent: 'primary', size: 'md', class: 'uppercase' },
      { intent: 'secondary', size: 'sm', class: 'font-normal' }
    ],
    defaultVariants: {
      intent: 'primary',
      size: 'md',
    },
  }
);

type ButtonProps = VariantProps<typeof buttonVariants>;

// Example usage:
console.log(buttonVariants({ intent: 'primary', size: 'lg' }));
// Expected output: 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none bg-blue-600 text-white hover:bg-blue-700 h-11 px-6 py-3'

console.log(buttonVariants({ intent: 'secondary', size: 'sm' }));
// Expected output: 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none bg-gray-200 text-gray-900 hover:bg-gray-300 h-9 px-3 py-2 font-normal'

console.log(buttonVariants({}));
// Expected output (default variants): 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none bg-blue-600 text-white hover:bg-blue-700 h-10 px-4 py-2 uppercase'

view raw JSON →