classcat vs classnames vs clsx
Conditional CSS Class Composition in JavaScript Applications
classcatclassnamesclsxSimilar Packages:

Conditional CSS Class Composition in JavaScript Applications

classcat, classnames, and clsx are lightweight utility libraries designed to help developers conditionally combine CSS class names in JavaScript applications. They simplify the process of dynamically building class strings by accepting a mix of strings, arrays, and objects — where object keys represent class names and boolean values determine whether those classes should be included. This avoids manual string concatenation and reduces boilerplate when working with component-based UI frameworks like React, Vue, or Svelte.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
classcat09125.19 kB12 years agoMIT
classnames017,79223.6 kB112 years agoMIT
clsx09,7818.55 kB222 years agoMIT

Conditional CSS Class Composition: classcat vs classnames vs clsx

When building dynamic user interfaces, you often need to toggle CSS classes based on state, props, or context. Manually concatenating strings is error-prone and hard to read. That’s where classcat, classnames, and clsx come in — they provide clean, declarative ways to compose class names. While they solve the same problem, their APIs, performance characteristics, and design philosophies differ in subtle but important ways.

🧩 Core API Design: How You Pass Arguments

All three accept strings, arrays, and objects, but they handle nesting and falsy values differently.

classnames is the most permissive. It recursively flattens nested arrays and ignores falsy values (null, undefined, false, 0, '').

// classnames
import classNames from 'classnames';

const btnClass = classNames(
  'btn',
  { primary: true, disabled: false },
  ['extra', null, 'padding'],
  undefined
);
// → 'btn primary extra padding'

clsx mimics classnames’s behavior almost exactly but skips deep recursion for performance. It flattens only one level of arrays and still ignores falsy values.

// clsx
import clsx from 'clsx';

const btnClass = clsx(
  'btn',
  { primary: true, disabled: false },
  ['extra', null, 'padding'],
  undefined
);
// → 'btn primary extra padding'

classcat uses a stricter, array-only top-level interface. You must pass all arguments inside a single array. It does not support multiple top-level arguments or deep nesting.

// classcat
import cc from 'classcat';

const btnClass = cc([
  'btn',
  { primary: true, disabled: false },
  ['extra', 'padding'] // nested arrays are NOT flattened
]);
// → 'btn primary extra padding' ✅

// But this fails to flatten deeper:
cc(['a', [['b']]]); // → 'a b' in classnames/clsx, but 'a [object Object]' in classcat ❌

💡 Note: classcat does not flatten nested arrays beyond one level. Passing [['b']] results in unintended output.

⚡ Performance and Runtime Behavior

classcat is the fastest because it avoids recursion and uses a simple loop. It’s optimized for the common case: a flat list of strings and objects.

clsx is nearly as fast as classcat but maintains API parity with classnames for shallow inputs. It avoids the overhead of deep flattening, making it ideal for typical React component use cases.

classnames is the slowest due to its recursive flattening logic, which walks through arbitrarily deep arrays. This is rarely needed in practice and adds unnecessary cost.

In real-world apps, you’ll rarely notice the difference — but in hot paths (e.g., rendering thousands of list items), clsx or classcat can reduce CPU time.

📝 Developer Experience and Readability

classnames has the most familiar syntax. Many developers already know it from tutorials or legacy codebases. Its flexibility feels “safe” — you can throw anything at it, and it usually works.

// Feels natural to many
<button className={classNames('btn', props.className, { active: isActive })}>

clsx offers identical DX with better performance. If your team already uses classnames, switching to clsx is often a drop-in replacement.

// Same as above, but faster
<button className={clsx('btn', props.className, { active: isActive })}>

classcat requires wrapping everything in an array, which some find less ergonomic:

// More verbose at call site
<button className={cc(['btn', props.className, { active: isActive }])}>

This can feel unnatural if you’re used to variadic functions. However, it enforces consistency and makes the input structure explicit.

🧪 Edge Case Handling

All three ignore false, null, undefined, 0, and empty strings. But they diverge on edge inputs:

  • Numbers: classnames and clsx ignore 0 but include other numbers (e.g., 42 becomes '42'). classcat behaves the same.
  • Nested arrays: Only classnames fully flattens them. clsx flattens one level; classcat does not flatten nested arrays at all.
  • Objects with non-boolean values: All treat truthy values as “include” and falsy as “exclude.” So { foo: 'bar' } includes 'foo'; { foo: '' } excludes it.
// All produce 'btn 42'
classNames('btn', 42);
clsx('btn', 42);
cc(['btn', 42]);

// Only classnames fully flattens
classNames('a', [[['b']]]); // → 'a b'
clsx('a', [[['b']]]);      // → 'a [object Object]'
cc(['a', [[['b']]]]);     // → 'a [object Object]'

🛠️ When to Use Which?

Use clsx as your default

For most new projects — especially React apps — clsx hits the sweet spot: it’s fast, small, and reads exactly like classnames. If you’re starting fresh, there’s little reason not to choose it.

Stick with classnames for legacy or maximum compatibility

If you’re maintaining a large codebase already using classnames, or if you rely on deep array nesting (rare), migration may not be worth it. It’s stable, well-tested, and won’t break.

Choose classcat for extreme minimalism

If you’re building a design system, library, or performance-critical component where every byte and CPU cycle counts, and you can enforce a flat input structure, classcat delivers the leanest runtime.

✅ Summary Table

Featureclasscatclassnamesclsx
Top-level argsSingle array onlyVariadicVariadic
Nested array supportNone (shallow)Full recursionOne level only
PerformanceFastestSlowestVery fast
Bundle impactSmallestLargestVery small
DX familiarityLowerHighestHigh
Drop-in replacement for classnames?NoN/AYes (in most cases)

💡 Final Thought

These libraries are so small and focused that the “best” choice often comes down to team preference and project constraints. But if you’re starting today, clsx gives you the best balance of speed, clarity, and compatibility — making it the smart default for modern frontend work.

How to Choose: classcat vs classnames vs clsx

  • classcat:

    Choose classcat if you prioritize minimal runtime overhead and are comfortable with a more compact, array-centric API. It’s ideal for performance-sensitive environments (like micro-frontends or design systems) where bundle size and execution speed matter, and you don’t need support for nested arrays or complex object structures beyond flat key-value pairs.

  • classnames:

    Choose classnames if you’re working on a legacy codebase or need maximum compatibility and familiarity. It’s the most widely adopted option with extensive documentation and community examples, and it handles deeply nested arrays and mixed-type inputs robustly. However, its slightly heavier implementation may be unnecessary for new projects focused on minimalism.

  • clsx:

    Choose clsx if you want a modern, fast, and clean alternative that matches classnames’s intuitive API while being significantly more optimized. It’s an excellent default choice for new React or Vite-based projects, offering the same developer experience as classnames but with better performance and smaller footprint, without sacrificing readability or flexibility.

README for classcat

Classcat

Build a class attribute string quickly.

  • Framework agnostic, reusable, plain vanilla JavaScript.
  • Up to 2.5x faster than alternatives.
  • 217 B (minified+gzipped). 👌

This module makes it easy to build a space-delimited class attribute string from an object or array of CSS class names. Just pair each class with a boolean value to add or remove them conditionally.

import cc from "classcat"

export const ToggleButton = ({ isOn, toggle }) => (
  <div className="btn" onClick={() => toggle(!isOn)}>
    <div
      className={cc({
        circle: true,
        off: !isOn,
        on: isOn,
      })}
    />
    <span className={cc({ textOff: !isOn })}>{isOn ? "ON" : "OFF"}</span>
  </div>
)

Try with React, lit-html, Mithril, Superfine

Installation

npm install classcat

Or without a build step—import it right in your browser.

<script type="module">
  import cc from "https://unpkg.com/classcat"
</script>

API

cc(names)

cc(names: string | number | object | array): string
import cc from "classcat"

cc("elf") //=> "elf"

cc(["elf", "orc", "gnome"]) //=> "elf orc gnome"

cc({
  elf: false,
  orc: null,
  gnome: undefined,
}) //=> ""

cc({
  elf: true,
  orc: false,
  gnome: true,
}) //=> "elf gnome"

cc([
  {
    elf: true,
    orc: false,
  },
  "gnome",
]) //=> "elf gnome"

Benchmarks

npm --prefix bench start

License

MIT