Variants
Define base styles and variant keys like color, size, and disabled on a tv recipe for typed call-site props.
Variants turn one component definition into many visual states. You set a base — shared classes every call site gets — then add keys like variant, size, or disabled.
Base + variants
import { tv } from 'tailwind-variants';const button = tv({ base: 'inline-flex cursor-pointer items-center justify-center rounded-full px-4 py-2 text-sm font-medium select-none transition-colors', variants: { variant: { primary: 'bg-zinc-900 text-white hover:bg-zinc-800', secondary: 'border border-zinc-300 bg-zinc-50 text-zinc-900 hover:bg-zinc-100', tertiary: 'text-zinc-700 hover:bg-zinc-200/70 hover:text-zinc-950' } }});button({ variant: 'secondary' });Base classes apply first. Variant classes layer on top. Conflicting utilities resolve automatically in the default build.
Multiple variants
Combine as many variant keys as you need. Each key is an independent axis (color × size × disabled):
const button = tv({ base: 'inline-flex cursor-pointer items-center justify-center rounded-full font-medium select-none transition-colors', variants: { variant: { primary: 'bg-zinc-900 text-white', secondary: 'border border-zinc-300 bg-zinc-50 text-zinc-900', tertiary: 'text-zinc-700' }, size: { sm: 'h-8 px-3 text-sm', md: 'h-10 px-4 text-sm', lg: 'h-11 px-5 text-base' } }});button({ variant: 'primary', size: 'lg' });Boolean variants
Use true / false keys for state flags:
const button = tv({ base: 'inline-flex cursor-pointer items-center justify-center rounded-full bg-zinc-900 px-4 py-2 text-sm font-medium text-white select-none', variants: { disabled: { true: 'cursor-not-allowed opacity-45', false: '' } }});button({ disabled: true });Boolean variants work well for disabled, active, loading, or feature toggles.
Array values
Variant values accept arrays. TV flattens them like clsx:
const badge = tv({ base: 'inline-flex select-none items-center rounded-full border px-2.5 py-0.5 text-sm font-medium', variants: { color: { primary: ['bg-zinc-100', 'text-zinc-800', 'border-zinc-300'] } }});