Collected · classnames · MIT
Temporary exhibition · Hall of the Commons
Fifty Lines, Everywhere
JavaScript·2014·50 lines·882 bytes
const hasOwn = {}.hasOwnProperty;
export default function classNames () {
let classes = '';
for (let i = 0; i < arguments.length; i++) {
const arg = arguments[i];
if (arg) {
classes = appendClass(classes, parseValue(arg));
}
}
return classes;
}
function parseValue (arg) {
if (typeof arg === 'string') {
return arg;
}
if (typeof arg !== 'object') {
return '';
}
if (Array.isArray(arg)) {
return classNames.apply(null, arg);
}
if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
return arg.toString();
}
let classes = '';
for (const key in arg) {
if (hasOwn.call(arg, key) && arg[key]) {
classes = appendClass(classes, key);
}
}
return classes;
}
function appendClass (value, newClass) {
if (!newClass) {
return value;
}
return value ? (value + ' ' + newClass) : newClass;
}Curator’s note
Fifty lines that do something you could write yourself in five minutes, and which have been downloaded more times than almost anything else in this building. That gap is the exhibit.
What it does is join strings, skipping the falsy ones and unpacking objects whose keys are class names and whose values decide inclusion. The reason it exists is that the alternative at every call site is a small pile of ternaries and template literals that produces "btn active" with a double space, or "btn false", or "btn undefined" — none of which break loudly, and all of which are tedious to get right a hundred times in a codebase.
The interesting decisions are all refusals. It does not deduplicate. It does not sort. It does not validate that anything is a plausible class name. It does not care about CSS at all — hand it numbers and it will happily join those. Every one of those would be defensible, and every one would make it something you had to think about rather than something you reach for.
Note the Object.prototype.hasOwnProperty guard rather than a bare in. Fifty lines and it still takes the trouble not to be confused by an object that inherits a toString. That is what separates a utility that ships everywhere from a snippet in someone's helpers file.
There is a version of software history where this file never gets written and every React codebase carries its own slightly-wrong copy. It is worth standing in front of as a reminder that the most-used code is rarely the most interesting code, and that this is fine.