Skip to content
Building UI Design Systems

Lesson 4 of 6 · 24 min

x
4/6

Lesson position in the course — not completion. Use Mark Complete to track finished lessons (saved in this browser).

Building & Packaging Reusable Components

Architecting a scalable component library requires defining consistent prop APIs across all elements. Using polymorphic composition via Radix UI's <Slot> primitive (or the asChild prop pattern), components can delegate their DOM element tag to child elements while retaining their design styles.

Component variants should be managed systematically using class-variance-authority (cva). cva lets you define base styles, variant combinations (default, outline, destructive), sizes (sm, md, lg), and default prop mappings cleanly in TypeScript.

Before
Complex Nested If/Else Style Logic
1// ❌ Cluttered variant conditional logic2const getStyles = (variant, size) => {3  let s = "font-medium rounded ";4  if (variant === "primary") s += "bg-blue-600 text-white ";5  if (size === "sm") s += "text-sm px-2 ";6  return s;7};
After
Type-Safe Class Variance Authority (`cva`)
1import { cva, type VariantProps } from 'class-variance-authority';2 3const buttonVariants = cva(4  'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none',5  {6    variants: {7      variant: {8        default: 'bg-primary text-primary-foreground hover:bg-primary/90',9        destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',10        outline: 'border border-input bg-background hover:bg-accent',11      },12      size: {13        default: 'h-10 px-4 py-2',14        sm: 'h-9 rounded-md px-3',15        lg: 'h-11 rounded-md px-8',16      },17    },18    defaultVariants: {19      variant: 'default',20      size: 'default',21    },22  }23);

Exercise

Define a badgeVariants definition using cva supporting variant (default, secondary, outline) and export its VariantProps.

Check your understanding

  • What problem does class-variance-authority (cva) solve?Show answer

    Answer

    It provides a structured, type-safe API for defining component variants and default prop mappings without dirty ternary strings.
  • What does the asChild prop pattern enable?Show answer

    Answer

    It allows a component to merge its styles and behavior onto its direct child element instead of rendering an extra wrapper DOM node.
Previous

Progress is saved in this browser.

Next Lesson