Prerequisite Knowledge#
When using Tailwind CSS, combining class names is a common requirement. We often use a method called cn
to efficiently and elegantly manage classes, which typically encapsulates the following two commonly used tools:
1. clsx
#
- A lighter and more performant alternative to
classnames
. - Supports combining classes in the form of strings, objects, arrays, etc.
- Example:
clsx('btn', { 'btn-primary': isPrimary }) // => 'btn btn-primary'
2. tailwind-merge
(abbreviated as twMerge
)#
- Used to merge conflicting Tailwind CSS class names.
- Handles conflicts like
text-sm text-lg
, retaining the last class with higher priority. - Example:
twMerge('text-sm text-lg') // => 'text-lg'
3. cn
Method Encapsulation#
Combining the above two libraries into a unified cn
method:
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(...inputs))
}
4. Why should className
be the last parameter?#
To ensure that user-provided styles can override default styles, it is recommended to place className
last:
cn('default-class', className)
Because twMerge
merges from left to right, the later classes will take precedence. If the order is reversed, the component's default styles may override user intentions.
What Did I Do?#
To maintain consistency in the practice of placing className
at the end of the parameters, I developed an ESLint plugin for static detection and automatic fixing with the help of Grok3, compatible with Eslint v7-v9.
Source:#
eslint-plugin-classname-arg-last
Example:#
// Error example
cn(className, 'text-sm')
// Correct usage
cn('text-sm', className)
Configuration:#
Add to .eslintrc.js
:
module.exports = {
// ...
plugins: ['classname-arg-last'],
rules: {
'classname-arg-last/classname-arg-last': 'error',
},
}
With this plugin, you can standardize practices during the development phase and reduce style conflicts caused by order issues.