cx - Class Name Merger
A lightweight utility for merging class names. Supports strings, arrays, objects, and nested combinations.
Import
Section titled “Import”import { cx } from 'css-variants'Type Signature
Section titled “Type Signature”function cx(...args: ClassValue[]): string
type ClassValue = | string | number | bigint | boolean | null | undefined | ClassDictionary | ClassValue[]
type ClassDictionary = Record<string, unknown>Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
...args | ClassValue[] | Any number of class values to merge |
Return Value
Section titled “Return Value”Returns a single string with all valid class names joined by spaces.
Examples
Section titled “Examples”Basic Usage
Section titled “Basic Usage”import { cx } from 'css-variants'
cx('foo', 'bar')// => 'foo bar'
cx('foo', null, 'bar', undefined, 'baz')// => 'foo bar baz'
cx('foo', false && 'bar', 'baz')// => 'foo baz'Object Syntax
Section titled “Object Syntax”Conditionally include classes based on boolean values:
cx({ foo: true, bar: false, baz: true })// => 'foo baz'
cx('base', { active: isActive, disabled: isDisabled })// => 'base active' (if isActive is true and isDisabled is false)Array Syntax
Section titled “Array Syntax”Group related classes together:
cx(['foo', 'bar'])// => 'foo bar'
cx(['foo', null, 'bar'])// => 'foo bar'
cx(['text-lg', 'font-bold'], 'text-blue-600')// => 'text-lg font-bold text-blue-600'Mixed Syntax
Section titled “Mixed Syntax”Combine all formats as needed:
cx( 'base-class', ['array-class-1', 'array-class-2'], { conditional: true, ignored: false }, condition && 'conditional-class', 42, null, undefined)// => 'base-class array-class-1 array-class-2 conditional conditional-class 42'Nested Arrays
Section titled “Nested Arrays”Arrays can be nested to any depth:
cx('a', ['b', ['c', 'd']], 'e')// => 'a b c d e'Number Values
Section titled “Number Values”Numbers are converted to strings:
cx('z-index-', 10)// => 'z-index- 10'
cx(1, 2, 3)// => '1 2 3'React Example
Section titled “React Example”function Component({ isActive, isDisabled, className }) { return ( <div className={cx( 'base-class', isActive && 'active', isDisabled && 'disabled', className )} > Content </div> )}
// Usage<Component isActive className="custom-class" />// => <div className="base-class active custom-class">Vue Example
Section titled “Vue Example”<template> <div :class="classes">Content</div></template>
<script setup>import { computed } from 'vue'import { cx } from 'css-variants'
const props = defineProps(['isActive', 'isDisabled'])
const classes = computed(() => cx( 'base-class', { active: props.isActive, disabled: props.isDisabled } ))</script>Common Patterns
Section titled “Common Patterns”Conditional Styling
Section titled “Conditional Styling”const buttonClass = cx( 'btn', variant === 'primary' && 'btn-primary', variant === 'secondary' && 'btn-secondary', size === 'large' && 'btn-lg', disabled && 'btn-disabled')Merging User Classes
Section titled “Merging User Classes”function Button({ className, ...props }) { return ( <button className={cx('px-4 py-2 rounded', className)} {...props} /> )}Responsive Classes
Section titled “Responsive Classes”const containerClass = cx( 'container mx-auto', ['px-4', 'sm:px-6', 'lg:px-8'], fullWidth && 'max-w-none')State-Based Classes
Section titled “State-Based Classes”const inputClass = cx( 'border rounded px-3 py-2', { 'border-gray-300 focus:border-blue-500': !error, 'border-red-500 focus:border-red-600': error, 'bg-gray-100 cursor-not-allowed': disabled, })Comparison with Other Libraries
Section titled “Comparison with Other Libraries”cx is similar to clsx and classnames, but optimized for css-variants:
// css-variants cximport { cx } from 'css-variants'
// clsximport clsx from 'clsx'
// classnamesimport classNames from 'classnames'
// All produce the same output for basic usagecx('foo', 'bar') // => 'foo bar'clsx('foo', 'bar') // => 'foo bar'classNames('foo', 'bar') // => 'foo bar'Integration with tailwind-merge
Section titled “Integration with tailwind-merge”For Tailwind CSS projects, combine cx with tailwind-merge to handle class conflicts:
import { cx } from 'css-variants'import { twMerge } from 'tailwind-merge'
// Create a custom mergerconst cn: typeof cx = (...args) => twMerge(cx(...args))
// Now conflicting Tailwind classes are resolvedcn('px-4 py-2', 'px-6')// => 'py-2 px-6' (px-4 is removed, px-6 wins)See the Tailwind CSS Integration guide for more details.