Componentizando com Tailwind
O que fazer quando a mesma pilha de classes se repete em vários lugares — cva e clsx na prática.
Depois de algumas telas, um padrão aparece: o mesmo botão, com as mesmas 8 classes, colado em vários lugares. A resposta certa não é criar uma classe CSS nova pra "resolver" isso — é extrair um componente React. O Tailwind resolve estilo; componente resolve repetição.
function Button({ children, onClick }: { children: React.ReactNode; onClick?: () => void }) {
return (
<button
onClick={onClick}
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg font-semibold transition"
>
{children}
</button>
);
}E quando o componente tem variantes?
Um botão "primário" e um "de perigo" compartilham metade das classes. Concatenar strings na mão funciona até parar de funcionar — é aí que entram duas bibliotecas pequenas: clsx (junta classes condicionalmente) e class-variance-authority (cva, define variantes tipadas).
import { cva, type VariantProps } from "class-variance-authority";
const button = cva("px-4 py-2 rounded-lg font-semibold transition", {
variants: {
intent: {
primary: "bg-blue-600 hover:bg-blue-700 text-white",
danger: "bg-red-600 hover:bg-red-700 text-white",
},
},
defaultVariants: { intent: "primary" },
});
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof button>;
function Button({ intent, className, ...props }: ButtonProps) {
return <button className={button({ intent, className })} {...props} />;
}Agora <Button intent="danger">Excluir</Button> troca a variante inteira sem repetir uma classe sequer, e o TypeScript te avisa se você passar um intent que não existe.
Exercício
Adicione uma terceira variante ghost (fundo transparente, texto azul, hover:bg-blue-50) e use-a num terceiro botão.