Skip to main content

cx

cx is a re-export of clsx. It accepts any number of class values, filters falsy entries, and returns a single concatenated string.

Signature

import { cx } from "cva";

interface CX {
  (...inputs: ClassValue[]): string;
}

export type CXOptions = Parameters<CX>; // ClassValue[]
export type CXReturn = ReturnType<CX>;  // string

Parameters

...inputs
ClassValue[]
Any number of class values. A ClassValue is the union:
type ClassValue =
  | ClassArray
  | ClassDictionary
  | string
  | number
  | bigint
  | null
  | boolean
  | undefined;

type ClassDictionary = Record<string, any>;
type ClassArray = ClassValue[];
Falsy values (false, null, undefined, 0, "") are ignored.

Return value

A string of space-separated class names.

Usage

Strings
import { cx } from "cva";

cx("font-bold", "text-lg");
// => "font-bold text-lg"
Arrays
cx(["font-bold", "text-lg"], "rounded");
// => "font-bold text-lg rounded"
Conditional objects
cx("base", { "text-blue-500": true, "text-red-500": false });
// => "base text-blue-500"
Mixed
const isActive = true;
const isDisabled = false;

cx(
  "btn",
  isActive && "btn--active",
  isDisabled && "btn--disabled",
  ["rounded", "border"],
);
// => "btn btn--active rounded border"

Using cx with defineConfig

When you create a custom cx via defineConfig, the onComplete hook runs after all inputs are concatenated. This is how tailwind-merge integration works:
lib/utils.ts
import { defineConfig } from "cva";
import { twMerge } from "tailwind-merge";

export const { cx } = defineConfig({
  hooks: {
    onComplete: (className) => twMerge(className),
  },
});
import { cx } from "../lib/utils";

// tailwind-merge resolves conflicts before returning
cx("px-4 py-2", "p-8");
// => "p-8"
See defineConfig for the full configuration API.

Build docs developers (and LLMs) love