AniUI Academy

Build the cn() class name merger

easyDOM & Utilities+150 XPPractice

Every component library ships a version of this — clsx, classnames, the cn() in a shadcn project. You call it with a base class, a conditional object for state, and sometimes an array from a parent, and it hands back one flat string for className.

Implement cn(...args). Each argument may be a string, a number, a plain object, an array (nested to any depth), or a falsy value. Strings are used as written. Numbers are converted with String(n). For a plain object, each key is included when its value is truthy, in the object's own key order. Arrays are walked recursively in order. Falsy values — null, undefined, false, 0, "" and NaN — are skipped everywhere they appear, including inside arrays and as object values.

Return the collected class names joined by a single space, in the exact left-to-right order they were encountered. Duplicates are kept, not removed. There is no leading or trailing space, and when nothing survives you return the empty string "".

Arguments

...argsArray<string | number | object | Array | falsy>
Any number of class name inputs: strings, numbers, plain objects whose truthy keys contribute class names, arrays nested to any depth, or falsy values that are skipped.

Returns

string — The resolved class names joined by a single space in encounter order, with duplicates preserved. Returns "" when nothing survives.

Examples

cn("btn", "btn-primary");
// => "btn btn-primary"

Plain strings are joined with a single space.

cn("btn", { active: true, disabled: false, "is-loading": 1 });
// => "btn active is-loading"

Object keys appear only when the value is truthy, in key order.

cn(["a", ["b", null, ["c"]]], "d");
// => "a b c d"

Arrays are walked recursively; falsy entries are skipped.

cn(null, undefined, false, "", 0, NaN, [], {});
// => ""

When every input is falsy or empty, the result is the empty string.

What it has to do

  • Accept any number of arguments: strings, numbers, plain objects, and arrays nested to any depth.
  • Include an object's key only when its value is truthy, keeping the object's key order.
  • Skip every falsy value, including 0, "", NaN, false, null and undefined.
  • Join with a single space, preserve encounter order, and keep duplicate class names.
  • Return "" when no class names survive.

Notes

  • Encounter order is left-to-right across arguments, deep-first inside arrays, and insertion order for object keys — the same order a reader would read the source.
  • Duplicates are deliberately kept so a caller can override an earlier class with a later one when the final CSS cascade follows source order.

Stuck? The javascript course covers everything this challenge needs.

Try it yourself
Loading playground...
5 tests · +150 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.