Skip to content

Polymorphism

cva components are polymorphic (and framework-agnostic) by default; just apply the class to your preferred HTML element…

import { button } from "./components/button";
export default () => (
<a className={button()} href="/sign-up">
Sign up
</a>
);

Alternative Approaches

React

If you’d prefer to use a React-based API, cva strongly recommends using @radix-ui’s Slot component to create your own asChild prop.

./components/button.tsx
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "cva";
const button = cva({
base: "button",
variants: {
intent: {
primary: [
"bg-blue-500 text-white",
"text-white",
"border-transparent",
"hover:bg-blue-600",
],
secondary: [
"bg-white",
"text-gray-800",
"border-gray-400",
"hover:bg-gray-100",
],
},
},
defaultVariants: {
intent: "primary",
},
});
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof button> {
asChild?: boolean;
}
export const Button: React.FC<ButtonProps> = ({
asChild,
className,
intent,
...props
}) => {
const Comp = asChild ? Slot : "button";
return <Comp className={button({ intent, className })} {...props} />;
};

Usage

import { Button } from "./components/button";
// Renders:
// <a href="/sign-up" class="bg-blue-500 text-white text-white border-transparent hover:bg-blue-600">
// Sign up
// </a>
export default () => (
<Button asChild>
<a href="/sign-up">Sign up</a>
</Button>
);