Skip to Content

API Reference

Reference for tv, createTV, createCN, VariantProps, cn/cx/cnMerge, and configuration types exported by Tailwind Variants.

Condensed reference for tailwind-variants v4.0.2.

import {
  tv,
  createTV,
  cn,
  cnMerge,
  cx,
  twMerge,
  twJoin,
  clsx,
  type VariantProps
} from 'tailwind-variants';

Lite build:

import { tv, createTV, createCN, cn, cx } from 'tailwind-variants/lite';

Entries

EntryShips
tailwind-variantstv, createTV, cn, cnMerge, cx, defaultConfig, plus twMerge, twJoin, clsx. Ships the built-in merge table
tailwind-variants/mergetwMerge, twJoin, clsx, cn, cx. The merger without the recipe runtime. No tv
tailwind-variants/configEverything on the default entry, with twMergeConfig support, plus createTwMerge, extendTailwindMerge, createTailwindMerge, fromTheme, validators, mergeConfigs, getDefaultConfig. Includes the table compiler, so it is the largest entry
tailwind-variants/litetv, createTV, createCN, cn, cx. No table, so it is the smallest entry. Bring your own twMerge

clsx is also the default export of the default and merge entries, so tailwind-merge and clsx can be aliased to this package. See Migration.

tv

Creates a variant recipe. Returns a callable function plus metadata.

const button = tv(options, config?);

button({ variant: 'primary' }); // => string (no slots)
button({ variant: 'primary' }).base(); // => string (with slots)

button.base;              // resolved base string
button.slots;             // slot class map
button.variants;          // variant definitions
button.variantKeys;       // ['variant', 'size', ...]
button.defaultVariants;   // { variant: 'primary', ... }
button.compoundVariants;  // array
button.compoundSlots;     // array

Options

type TVOptions = {
  extend?: TVReturnType | TVReturnType[];
  base?: ClassValue;
  slots?: Record<string, ClassValue>;
  variants?: Record<string, Record<string, ClassValue>>;
  defaultVariants?: Record<string, ClassValue>;
  compoundVariants?: Array<Record<string, unknown> & ClassProp>;
  compoundSlots?: Array<Record<string, unknown> & ClassProp>;
};
OptionDescription
extendMerge parent recipes' base, slots, variants, defaults, compounds; non-empty arrays merge left to right
baseShared classes for every call
slotsNamed parts; {} enables implicit base slot only
variantsVariant axes; per-slot objects when using slots
defaultVariantsValues applied when keys are omitted
compoundVariantsExtra classes when multiple conditions match
compoundSlotsExtra classes on specific slots when conditions match

Config (2nd argument)

// tailwind-variants
type TVConfig = {
  twMerge?: boolean | TwMergeFn;
  debug?: boolean; // experimental; dev and browser only
};

// tailwind-variants/config
type TVCustomConfig = TVConfig & {
  twMergeConfig?: TWMergeConfig;
};

debug logs how each invocation resolves; it is stripped from production builds. See Debugging.

twMergeConfig is only supported by tailwind-variants/config. The default entry throws when a recipe is defined with it. See Configuration.

Lite config is { twMerge?: boolean | TwMergeFn } only. A function twMerge is invoked. twMergeConfig and debug are not available on lite.

Call-site props

type ClassProp = {
  class?: ClassValue;
  className?: ClassValue;
};

Pass variant keys plus optional class / className overrides.

createTV

Returns a tv with default config:

import { createTV } from 'tailwind-variants';

const tv = createTV({ twMerge: true });

For twMergeConfig, import from the config entry:

import { createTV } from 'tailwind-variants/config';

const tv = createTV({
  twMerge: true,
  twMergeConfig: { extend: { /* ... */ } }
});

On /lite, pass a merge function. createTV does not bind cn. See createCN below.

cn

Concatenate and merge with default config:

cn('px-2', 'px-4'); // => "px-4"
cn('text-sm', { 'font-bold': true }); // => "text-sm font-bold"
cn(); // => "" (never undefined)

On the default build, cn returns a string directly. An empty class list yields "". cn follows clsx, so a numeric 0 among other arguments is dropped and a lone 0 is "0". Lite exports a curried cn that joins; prefer createCN when injecting a merger.

createCN

Lite only. Returns a string cn bound to a merge function. createTV does not bind cn:

import { createCN, createTV } from 'tailwind-variants/lite';
import { twMerge } from 'tailwind-merge';

export const tv = createTV({ twMerge });
export const cn = createCN({ twMerge });

Without a function, createCN() is cx.

cnMerge

Concatenate with optional per-call config:

cnMerge('px-2', 'px-4')(); // => "px-4"
cnMerge('px-2', 'px-4')({ twMerge: false }); // => "px-2 px-4"

Default and config entries only. Not exported from /lite. The config accepts twMerge. twMergeConfig is accepted by cnMerge from tailwind-variants/config.

cx

Lightweight concat without conflict resolution:

cx('text-blue-500', 'text-red-500'); // => "text-blue-500 text-red-500"

Available in default and lite builds. Replaces deprecated cnBase. cx normalizes whitespace and keeps a numeric 0.

twMerge, twJoin, clsx

Merge utilities exported from the default and /merge entries:

import { twMerge, twJoin, clsx } from 'tailwind-variants';
// or
import { twMerge, twJoin, clsx } from 'tailwind-variants/merge';

twMerge('px-2', 'px-4'); // => "px-4"
twJoin('a', ['b'], false && 'c'); // => "a b"
clsx('a', 0, { b: true }); // => "a b"

twMerge accepts strings and nested arrays, no object syntax. twJoin joins strings and arrays. clsx follows clsx. See Class Resolution.

VariantProps

Extract variant prop types from a recipe:

type Props = VariantProps<typeof button>;
// { variant?: 'primary' | 'secondary' | 'tertiary'; size?: 'sm' | 'md' }

Types

TypeDescription
ClassValuestring | string[] | Record<string, boolean> | null | undefined
VariantProps<T>Inferred variant keys from recipe T
TVReturnTypeReturn type of tv() — callable + metadata
TVConfigDefault-entry config: twMerge, debug
TVCustomConfigConfig-entry config: TVConfig plus twMergeConfig
TWMergeConfigShape of twMergeConfig: { extend, override, cacheSize, prefix } or a full config
TVLiteConfigLite config: { twMerge?: boolean | TwMergeFn }
TwMergeFn(classList: string) => string

Slotted return type

When slots is defined, calling the recipe returns an object of functions:

const { base, title } = alert({ color: 'danger' });
base({ class: 'mt-2' }); // => string

Each slot function accepts class / className and variant overrides scoped to that slot.

On this page