Replace clsx, classnames or classcat with your own little helper

Gustavo Guichard (Guga) - Aug 18 '21 - - Dev Community

Have you ever taken a break from programming, only to return and wrestle with package updates, outdated dependencies, or broken code? This issue often arises if your project heavily relies on a multitude of libraries and packages. In such instances, you might want to consider reducing the number of external imports your project utilizes.

We encountered this issue recently and perceived it as an opportunity to write a small helper utility that could replace a popular package.
Today, I'll be replacing a widely-used package called clsx (or classnames, etc) with a tiny function.

Planning

With clsx, you can pass a mix of strings, objects, arrays, and it consistently resolves to a string of classes to be used in your elements. If you're using a framework like Tailwind, where everything is accomplished through classes, you likely depend heavily on this function.

However, my colleagues and I rarely used it with objects.

Instead of this:

clsx('base', undefined, ['more', 'classes'], {
  'bg-red': hasError,
  'pointer-events-none': !isEnabled,
  'font-semibold': isTitle,
  'font-normal': !isTitle,
})

// Result: "base more classes bg-red font-normal"
Enter fullscreen mode Exit fullscreen mode

We prefer an API like:

cx('base', undefined, ['more', 'classes'],
  hasError && 'bg-red',
  isEnabled || 'pointer-events-none',
  isTitle ? 'font-semibold' : 'font-normal'
)

// Result: "base more classes bg-red font-normal"
Enter fullscreen mode Exit fullscreen mode

In fact, with the addition of the || operator, the final API proved to be even better suited for our needs.

The implementation

The final version of our function, which is just a few lines of code, accepts an array of unknown values, filters for strings, joins them with a space, and trims any leading or trailing whitespace:

function cx(...args: unknown[]) {
  return args
    .flat()
    .filter(x => typeof x === 'string')
    .join(' ')
    .trim()
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Think twice before adding yet another package. Sometimes, all you need are a few lines of code - which might be fewer than the additions made to your package-lock.json at the end of the day. Always consider whether you can achieve the same functionality with a simpler, custom solution.

. . . . . . . . . . . . . . . . . . . . . . . . .