Wednesday, September 28, 2022

Multiple className values in React

React offers basically zero support for CSS. Personally I’m fond of CSS Modules along with SCSS instead of the slow CSS-in-JS solutions out there. Still, we need a way to manage multiple classes in className property.

In order to mitigate this problem, I wrote a TypeScript function to deal with sequential or conditional class names situations:

/**
 * Generates the className attribute with the given class names.
 */
export function cn(
	...items: (
		string
		| null
		| undefined
		| (string | null | undefined)[]
		| Record<string, boolean>
	)[]
): string {
	let fin = ' ';
	for (const item of items) {
		if (typeof item === 'string') {
			fin += item + ' ';
		} else if (Array.isArray(item)) {
			for (const s of item) {
				if (typeof s === 'string') fin += s + ' ';
			}
		} else if (typeof item === 'object') {
			for (const key in item) {
				if (item[key]) fin += key + ' ';
			}
		}
	}
	return fin.substring(0, fin.length - 1);
}

Example using some argument possibilities:

import {cn} from '@/funcs';
import c from './App.module.css';
	
function App() {
	return <>
		<div className={cn( c.first )} />
		<div className={cn( c.first, c.second )} />
		<div className={cn( [c.first, c.second], c.third )} />
		<div className={cn( c.first, {[c.second]: true} )} />
	</>;
}

This covers all situations I ever faced, and it's basically the same syntax of classnames.

The spread operator version was added in February 9, 2023.

The multi-combination of parameters was added in December 30, 2024.