cls hot path: clsx vs cls

Benchmark publishedon

Description

Compares clsx src/index.js against src/utils/cls.js under string, object, array, nested array, and optional-value inputs. Numeric class names are not included because the local helper intentionally does not support them.

Setup

const CLS_RUNS = 1000;

const CLASS_OBJECT_PRIMARY = {
    active: true,
    disabled: false,
    loading: false,
};

const CLASS_OBJECT_STATUS = {
    focused: true,
    invalid: false,
    compact: true,
};

const CLASS_OBJECT_VARIANT = {
    success: true,
    danger: false,
    muted: true,
};

const CLASS_LIST_PRIMARY = ['panel', ['corners_rad2', false, 'elevated']];
const CLASS_LIST_STATUS = ['input_field', [null, 'with_icon', ['large']]];
const CLASS_LIST_VARIANT = ['tag', ['filled', undefined, ['interactive']]];

let clsSink = 0;

// clsx src/index.js, adapted for jsPerf by removing ESM exports.
// Source: https://raw.githubusercontent.com/lukeed/clsx/refs/heads/master/src/index.js
function toVal(mix) {

    var key, nested, output = '';

    if (typeof mix === 'string' || typeof mix === 'number') {
        output += mix;
    } else if (typeof mix === 'object') {
        if (Array.isArray(mix)) {
            var length = mix.length;

            for (key = 0; key < length; key++) {
                if (mix[key]) {
                    if (nested = toVal(mix[key])) {
                        output && (output += ' ');
                        output += nested;
                    }
                }
            }
        } else {
            for (nested in mix) {
                if (mix[nested]) {
                    output && (output += ' ');
                    output += nested;
                }
            }
        }
    }

    return output;
}

function clsxIndex() {

    var index = 0, value, resolved, output = '', length = arguments.length;

    for (; index < length; index++) {
        if (value = arguments[index]) {
            if (resolved = toVal(value)) {
                output && (output += ' ');
                output += resolved;
            }
        }
    }

    return output;
}

function cls() {

    let output = "";
    let value;

    for (let index = 0, length = arguments.length; index < length; index += 1) {
        value = arguments[index];

        if (!value) continue;

        if (typeof value === "string") {
            output = output ? output + " " + value : value;
            continue;
        }

        if (typeof value === "object") {
            output = appendClassValue(output, value);
        }
    }

    return output;
}

function appendClassValue(output, value) {

    if (!value) return output;

    if (typeof value === "string") {
        return output ? output + " " + value : value;
    }

    if (typeof value !== "object") return output;

    let key;

    if (Array.isArray(value)) {
        for (let index = 0, length = value.length; index < length; index += 1) {
            output = appendClassValue(output, value[index]);
        }

        return output;
    }

    for (key in value) {
        if (value[key]) {
            output = output ? output + " " + key : key;
        }
    }

    return output;
}

function runClsxIndex(current) {

    for (let index = 0; index < CLS_RUNS; index += 1) {
        current += clsxIndex('button', 'primary', 'compact').length;
        current += clsxIndex('button', CLASS_OBJECT_PRIMARY, 'corners_rad1').length;
        current += clsxIndex(CLASS_LIST_PRIMARY, CLASS_OBJECT_STATUS, 'visible').length;
        current += clsxIndex(false, 'segment_button', CLASS_LIST_STATUS, CLASS_OBJECT_VARIANT).length;
        current += clsxIndex(true, null, undefined, 'notification', CLASS_LIST_VARIANT).length;
    }

    return current;
}

function runCls(current) {

    for (let index = 0; index < CLS_RUNS; index += 1) {
        current += cls('button', 'primary', 'compact').length;
        current += cls('button', CLASS_OBJECT_PRIMARY, 'corners_rad1').length;
        current += cls(CLASS_LIST_PRIMARY, CLASS_OBJECT_STATUS, 'visible').length;
        current += cls(false, 'segment_button', CLASS_LIST_STATUS, CLASS_OBJECT_VARIANT).length;
        current += cls(true, null, undefined, 'notification', CLASS_LIST_VARIANT).length;
    }

    return current;
}

const EXPECTED_RESULT = 'button primary compact';
const EXPECTED_MIXED_RESULT = 'panel corners_rad2 elevated focused compact visible';

if (clsxIndex('button', 'primary', 'compact') !== EXPECTED_RESULT) throw new Error('clsxIndex string result mismatch');
if (cls('button', 'primary', 'compact') !== EXPECTED_RESULT) throw new Error('cls string result mismatch');
if (clsxIndex(CLASS_LIST_PRIMARY, CLASS_OBJECT_STATUS, 'visible') !== EXPECTED_MIXED_RESULT) throw new Error('clsxIndex mixed result mismatch');
if (cls(CLASS_LIST_PRIMARY, CLASS_OBJECT_STATUS, 'visible') !== EXPECTED_MIXED_RESULT) throw new Error('cls mixed result mismatch');

Test Runner

Initializing...

Testing in
Test CaseOps/sec
clsx
clsSink = runClsxIndex(clsSink);
ready
cls
clsSink = runCls(clsSink);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.

Revision 1
publishedon