clsx
는 조건부 스타일을 처리하는 아주 작은(239B) 유틸리티이다.
classnames
보다 작고 빠르다.
배열, 객체, 중첩 배열 전부 지원한다.
import clsx from "clsx";
// 문자열
clsx('foo', true && 'bar', 'baz');
//=> 'foo bar baz'
// 객체
clsx({ foo:true, bar:false, baz:isTrue() });
//=> 'foo baz'
// 배열
clsx(['foo', 0, false, 'bar']);
//=> 'foo bar'
clsx
를 사용하지 않았을 때 조건부 처리를 할 경우
const className =
"base" +
(isActive ? " bg-blue-500" : "") +
(!isEnabled ? " text-gray-400" : "");
빈 문자열이나 false, undefined, null등이 생기는데, clsx
는 이를 무시해준다.
classnames
은 clsx
와 비슷하지만 좀 더 오래되고 무거운 라이브러리이다.
문법상 거의 동일하니까 기존 코드에서 쓰고있는 경우를 제외하면 더 가볍고 빠른clsx
를 쓰자.
cva
는 class variance authority
의 약자로 안전한 UI 구성 요소를 구축하기 위한 라이브러리이다.
cva
는 작은 라이브러리지만 SSR/SSG 환경에서 사용하는게 좋다고 한다.
Although cva is a tiny library, it's best to use in a SSR/SSG environment – your user probably doesn't need this JavaScript, especially for static components.
CSR(클라이언트 사이드 렌더링)환경인 경우 cva
라이브러리 코드가 번들에 포함되고, 클래스 계산 로직이 브라우저에서 실행되어야 한다.
즉, 사용자는 CSS 클래스를 위해 추가적으로 JavaScript를 다운로드해야 하는 것이다.
반면 SSR(서버사이드 렌더링)이나 SSG(정적 사이트 생성)환경의 경우 서버에서 미리 계산된 클래스가 HTML에 포함되어 전달된다.
따라서 사용자는 별도의 JavaScript를 받을 필요가 없어 초기 로딩 성능이 향상되는 것이다.
그래서 정적 UI 중심이면 SSR/SSG에서 사용하는게 좋다!
import React from "react";
import { cva, type VariantProps } from "class-variance-authority";
const button = cva("button", {
variants: {
intent: {
primary: ["bg-blue-500", "text-white", "border-transparent"],
secondary: ["bg-white", "text-gray-800", "border-gray-400"],
},
size: {
small: ["text-sm", "py-1", "px-2"],
medium: ["text-base", "py-2", "px-4"],
},
disabled: {
false: null,
true: ["opacity-50", "cursor-not-allowed"],
},
},
// 특정 조합일 때만 적용할 클래스
compoundVariants: [
{
intent: "primary",
disabled: false,
class: "hover:bg-blue-600",
},
{
intent: "secondary",
disabled: false,
class: "hover:bg-gray-100",
},
{ intent: "primary", size: "medium", class: "uppercase" },
],
// 기본값
defaultVariants: {
disabled: false,
intent: "primary",
size: "medium",
},
});
export interface ButtonProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "disabled">,
VariantProps<typeof button> {}
export const Button: React.FC<ButtonProps> = ({
className,
intent,
size,
disabled,
...props
}) => (
<button
className={button({ intent, size, disabled, className })}
disabled={disabled || undefined}
{...props}
/>
);
tailwind의 클래스 이름이 겹칠때 중복을 제거해서 가장 마지막 클래스만 남겨주는 라이브러리이다.
import { twMerge } from "tailwind-merge";
twMerge("bg-red-500 bg-blue-500");
// => "bg-blue-500"
보통 twMerge와 clsx를 같이 사용한 유틸 함수를 만들어서 사용한다.
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(...inputs));
}
이렇게 하면 조건부 로직을 안전하게 처리할 수 있다!