When I saw the cn function being imported from @/lib/utils in shadcn-ui/ui source code, I assumed that this function’s name is derived from “shadcn” since it contains “cn’’and that it handles some core logic but turns out, it is a wrapper on top of clsx and twMerge. I questioned, Why? Why would you need such a wrapper?
To understand the reason behind this cn wrapper, you must first understand clsx and tailwind-merge.
Clsx
Clsx official docs definition is that it is a tiny (239B) utility for constructing className strings conditionally.
Also serves as a faster & smaller drop-in replacement for the classnames module.
Examples:
import clsx from 'clsx';
// or
import { clsx } from 'clsx';
// Strings (variadic)
clsx('foo', true && 'bar', 'baz');
//=> 'foo bar baz'
// Objects
clsx({ foo:true, bar:false, baz:isTrue() });
//=> 'foo baz'
// Objects (variadic)
clsx({ foo:true }, { bar:false }, null, { '--foobar':'hello' });
We all are familiar with the clsx package, it is used to render the classnames conditionally.
Tailwind-merge:
To be honest, I have never used the tailwind-merge package before. So I visited the official docs and learnt that it is a utility function to efficiently merge Tailwind CSS classes in JS without style conflicts.
Example:
import { twMerge } from 'tailwind-merge'
twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]')
// → 'hover:bg-dark-red p-3 bg-[#B91C1C]'
Connecting the dots:
It was at this point, it occurred to me that in shadcn-ui, clsx conditionally renders tailwind class names as strings and doing so could result in tailwind class name conflicts
Cn usage in shadcn-ui/ui:
I found the following files using cn function
https://github.com/search?q=repo%3Ashadcn-ui%2Fui+cn&type=code