@styled-cva/react
v0.7.1
Published
A typesafe, class-variance-authority-based, styled-components-like library for authoring React components
Maintainers
Readme
⚠️ starting from 0.4.x, styled-cva only supports React >=19.x. If you're on React 18, use the latest 0.3.x ⚠️
Why styled-cva?
styled-cva offers the best of both worlds: the expressiveness of styled-components and the power of class-variance-authority, all while being fully typesafe and optimized for Tailwind CSS.
- Familiar
styled-componentsAPI: If you love thetw.button...` syntax, you'll feel right at home. - Powered by CVA: Easily create components with complex variants.
- Fully Typesafe: Enjoy autocompletion and type-checking for all your component's variants.
- Seamless Tailwind CSS Integration:
styled-cvais designed to work perfectly with Tailwind CSS and the official Tailwind CSS VSCode extension. - Lightweight and Performant: No unnecessary abstractions, just the essentials to build beautiful and consistent UIs.
Features
- ✅ Supports all HTML elements.
- ✅ Full TypeScript support, including prop and variant inference.
- ✅ Re-exports
@styled-cva/coreutilities (cn,cva,isTaggedTemplateArg, …) from the package entry. - ✅
cvaandwithPropsfor powerful component customization. - ✅ Polymorphic
asprop to change the rendered element. - ✅ Smart prop filtering to avoid extraneous props on the DOM.
- ✅ Works with custom React components.
Installation
npm
npm i --save @styled-cva/reactpnpm
pnpm add @styled-cva/reactbun
bun add @styled-cva/reactUsage
Basic Example
Create a simple, styled button.
import tw from "@styled-cva/react";
const Button = tw.button`
bg-blue-500 text-white font-bold py-2 px-4 rounded
`;
// ...
<Button>Click Me</Button>;Variants with cva
Create a button with different variants.
import tw from "@styled-cva/react";
const Button = tw.button.cva(
"font-bold py-2 px-4 rounded", // base class
{
variants: {
// Use '$' to prevent the prop from being passed to the DOM
$variant: {
primary: "bg-blue-500 text-white",
secondary: "bg-gray-500 text-white",
},
$size: {
small: "text-sm",
large: "text-lg",
},
},
defaultVariants: {
$variant: "primary",
},
},
);
// ...
<Button $variant="secondary" $size="large">
Click Me
</Button>;withProps for Default Props
Apply default props to your component.
const PrimaryButton = tw.button
.cva("font-bold py-2 px-4 rounded", {
variants: {
$variant: {
primary: "bg-blue-500 text-white",
secondary: "bg-gray-500 text-white",
},
},
})
.withProps({
"data-testid": "my-button",
type: "button",
$variant: "primary", // Default variant
});
// ...
<PrimaryButton>Click Me</PrimaryButton>;Polymorphic as Prop
Render a different element or component.
import Link from "next/link";
import tw from "@styled-cva/react";
const Button = tw.button.cva("font-bold py-2 px-4 rounded", {
variants: {
$variant: {
primary: "bg-blue-500 text-white",
secondary: "bg-gray-500 text-white",
},
},
});
// ...
// Renders as an anchor tag
<Button $as="a" href="/some/url" $variant="primary">
I'm a link!
</Button>
// Renders as a Next.js Link component
<Button $as={Link} href="/some/url" $variant="secondary">
I'm a Next.js Link!
</Button>Type-safe wrapper with PolymorphicComponentProps
When $as is a custom React component (TanStack Router Link, Next.js Link, etc.) and you want a typed wrapper, compose the props with PolymorphicComponentProps. It automatically picks up every $-prefixed variant on the source component.
import Link, { type LinkProps } from "next/link";
import tw, { type PolymorphicComponentProps } from "@styled-cva/react";
const Button = tw.button.cva("btn", {
variants: {
$variant: { primary: "btn-primary", secondary: "btn-secondary" },
},
});
type ButtonLinkProps = PolymorphicComponentProps<typeof Button, typeof Link> &
LinkProps;
export const ButtonLink = (props: ButtonLinkProps) => (
<Button {...props} $as={Link} />
);
// <ButtonLink href="/about" $variant="primary">About</ButtonLink>Styling Custom Components
Create a styled component from a custom component that accepts a className prop.
import tw from "@styled-cva/react";
const MyButton = ({ className }: { className: string }) => {
return <button className={className}>Hello</button>;
};
const StyledButton = tw(MyButton)`text-red-500`;
// ...
<StyledButton />;VSCode intellisense
For tailwindcss extension support, add this to your vscode settings.json
// tailwindcss intelisense settings
"tailwindCSS.emmetCompletions": true,
"tailwindCSS.includeLanguages": {
"typescript": "javascript", // if you are using typescript
"typescriptreact": "javascript" // if you are using typescript with react
},
"tailwindCSS.experimental.classRegex": [
"tw`([^`]*)", // tw`...`
"tw\\.[^`]+`([^`]*)`", // tw.xxx<xxx>`...`
"tw\\(.*?\\).*?`([^`]*)", // tw(Component)<xxx>`...`
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
],
"editor.quickSuggestions": {
"strings": true // forces VS Code to trigger completions when editing "string" content
},Contributing
Contributions are welcome! Please open an issue or submit a pull request.
License
This project is dedicated to the public domain under The Unlicense. See LICENSE in this package.
