classNamesLoading saved progress…

classNames

Implement a small utility, conventionally named classnames, that takes any number of arguments — strings, numbers, objects, arrays — and folds them down to a single space-separated string of CSS class names. It's the library a million React components reach for to write className={cn('btn', { active, disabled })} without a .filter(Boolean).join(' ') salad. See the popular classnames package on npm for the canonical behavior you're recreating.

Signature

// Accepts any number of args of mixed types and returns a single string.
function classnames(...args: ClassValue[]): string;

type ClassValue =
  | string         // included as-is
  | number         // included as-is (yes, including 0)
  | null           // skipped
  | undefined      // skipped
  | boolean        // skipped (true and false alike)
  | { [key: string]: unknown }   // key included when value is truthy
  | ClassValue[];  // recursively flattened

Examples

classnames('btn', 'btn-primary');
// → 'btn btn-primary'

classnames('btn', { active: true, disabled: false }, ['lg', 'primary']);
// → 'btn active lg primary'
// Falsy values are dropped; nested arrays are flattened.
classnames('a', null, undefined, false, ['b', ['c', { d: true, e: 0 }]]);
// → 'a b c d'

// Numbers (including 0) are kept; empty strings are skipped.
classnames(0, 1, '', 'x');
// → '0 1 x'

Notes

  • Falsy skippingnull, undefined, false, and the empty string '' are all dropped from the output. They never appear, and they never produce stray spaces.
  • Numbers stay — every number, including 0, is converted to its string form and included. This matches the real classnames library.
  • Object keys — for plain objects, include the key when its value is truthy. { active: true, disabled: false, hidden: 0 } contributes only active.
  • Recursive arrays — arrays can nest arbitrarily deep. ['a', ['b', ['c']]] contributes a b c. The order of names in the output must match the left-to-right reading order of the input.
  • No deduping, no sortingclassnames('a', 'a') is 'a a', not 'a'. Output order mirrors input order.
  • Don't worry about — non-plain objects (Maps, class instances), Symbols as keys, or input validation. Assume the caller behaves.
Loading editor…