class-variance-authority

A tiny TypeScript utility for building type-safe, variant-driven className APIs on top of clsx.

Library
npm
v0.7.1
6,903 stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
82 /100 Excellent
Development Activity 96
Maintenance 84
Community 52
Maturity 56
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
87 /100 Excellent
Architecture 85
Code Quality 82
Innovation 88
Learning Curve 92

class-variance-authority (CVA) lets you define a component’s visual variants — size, intent, state, and any compound combination of them — as a single declarative config object, then resolves the right class string at call time based on the props passed in. It wraps clsx internally so you still get array/object/conditional class syntax for free, but adds a typed layer on top: TypeScript infers your component’s prop types directly from the variant config via the exported VariantProps helper, so variant names and their allowed values stay in sync between the styling definition and the component’s public API. Originally built for Stitches-style variant ergonomics without a CSS-in-JS runtime, CVA has become the de facto variant-composition primitive behind shadcn/ui and a large share of the Tailwind CSS component ecosystem.

What You Get

  • The cva() factory — declare base classes, variants, defaultVariants, and compoundVariants once, get back a function that resolves to a class string per call
  • cx — a direct re-export of clsx for plain conditional class concatenation outside of a variant config
  • VariantProps<T> — a TypeScript helper that derives a component’s prop types straight from its cva() config, so variant names/values can’t drift out of sync with the component signature
  • A class/className escape hatch on every generated function, so callers can always merge in ad-hoc classes on top of the resolved variant output
  • Zero CSS-in-JS runtime — output is a plain string, so it works with Tailwind, CSS Modules, vanilla-extract, or hand-written CSS equally well

Common Use Cases

  • Defining variant props (size, variant, disabled) for a shadcn/ui-style Button, Badge, or Alert component built on Tailwind CSS
  • Sharing one variant config between multiple sibling components (e.g. Button and IconButton) that need consistent size/intent options
  • Replacing hand-rolled clsx(…) ternary chains in design-system components with a single declarative config that’s easier to review and extend
  • Building framework-agnostic style logic (React, Vue, Svelte, Astro) since cva() returns a plain string and has no framework-specific dependency

Under The Hood

Architecture The core implementation lives entirely in packages/class-variance-authority/src/index.ts (~150 lines), exporting two primitives: cx (a direct re-export of clsx for raw class concatenation) and cva (a curried factory function). Calling cva(base, config) returns a props-accepting function; internally it walks config.variants via Object.keys, resolves each variant’s selected key against props or config.defaultVariants using a falsyToString helper (so false/0 variant keys aren’t coerced away), then reduces config.compoundVariants against the merged props+defaults to append additional classes, finally concatenating everything through clsx (cx) alongside any props.class/props.className escape hatch. There is no internal state, no classes, no external I/O — it’s a pure, synchronous class-string composition pipeline. Types (VariantProps, ConfigSchema, etc.) are isolated in src/types.ts and lean on TypeScript generics/conditional types to give callers compile-time-checked variant props. The repo is a pnpm workspace housing this package plus a parallel v1 rewrite (packages/cva, still in beta) and a docs Astro site (docs/), but the shipped runtime surface of class-variance-authority itself is intentionally minimal.

Tech Stack TypeScript-only source (99.75% per GitHub language stats). Single runtime dependency: clsx (^2.1.1). The build tool is tsdown (a Rolldown-based bundler) producing dual ESM/CJS output plus .d.ts types. Dev tooling includes @arethetypeswrong/core and publint for package-export correctness checks, size-limit enforcing a 1.2KB budget on dist/index.js, TypeScript 6.0.3 for type-checking, and react/react-dom as devDependencies only (used to type-test VariantProps against component prop shapes, not as a runtime dependency). Package manager is pnpm with a workspace tying the core package to the docs site and the parallel v1 rewrite.

Code Quality packages/class-variance-authority/src/index.test.ts is a single but substantial 1766-line test file exercising variant resolution, defaultVariants, compoundVariants (including array-valued compound matching), null-variant overrides, and TypeScript type inference via type-only assertions. The implementation favors small, named helper functions (falsyToString) and explicit reduce/map compositions over cleverness; there are no custom error classes or thrown exceptions because the API can’t fail at runtime — invalid variant keys are caught at the type level rather than checked at runtime, a deliberate zero-runtime-validation design suited to a hot-path styling utility.

API Design The public API is two functions (cx, cva) plus one exported type helper (VariantProps<T>), which keeps the learning curve nearly flat: define variants as a nested object literal, call the returned function with a subset of variant keys, get a class string back. VariantProps<typeof myCva> lets consumers derive component prop types directly from the variant config instead of duplicating them — the library’s signature ergonomic win, and exactly what shadcn/ui-style component libraries lean on. The class/className escape hatch is a small but consistent affordance for merging caller-supplied classes on every call site. Documentation lives off-repo at cva.style (an Astro site checked into docs/) rather than in the README, keeping the README itself terse — good for npm page browsing, though GitHub-only visitors get less immediate context than the docs site provides.

Used by 191 apps in this directory

TypeScript
98%
AGPL 3.0

Karakeep

Bookmarks Archiving

29,564

Self-hosted bookmark manager that captures links, notes, images, and PDFs with AI tagging, full-text search, and automatic archiving.

View details
88
Repo Health
76
Technical
66
Dependency
Built with
TypeScript 98%
Updated 3 days ago
Python
51%
AGPL 3.0

Khoj

AI Assistants · Knowledge Management · Productivity

37,614

A self-hostable AI second brain that chats with your documents, searches the web, builds custom agents, and runs entirely on your own LLM.

View details
63
Repo Health
82
Technical
66
Dependency
Built with
Python 51%
TypeScript 36%
Updated 2 months ago
TypeScript
98%
MIT

Kimi Code CLI

AI Agents · AI Code Assistants · Developer Tools

7,813

A single-binary, terminal-native coding agent that reads, edits, and runs code end to end, built by Moonshot AI for Kimi models but pluggable with Anthropic, OpenAI, and Google providers too.

View details
83
Repo Health
87
Technical
67
Dependency
Built with
TypeScript 98%
Updated today
TypeScript
39%
Apache 2.0

Label Studio

AI Development · Data Engineering

28,431

Label Studio is an open-source, multi-type data labeling platform that lets teams annotate images, text, audio, video, and time series data with a configurable XML-based UI and export annotations in formats ready for any ML framework.

View details
94
Repo Health
87
Technical
67
Dependency
Built with
TypeScript 39%
JavaScript 27%
Python 25%
Updated yesterday
TypeScript
64%
Apache 2.0

Laminar

AI Development · Monitoring

3,368

Open-source observability platform purpose-built for AI agents — trace, evaluate, debug, and monitor at scale with SQL access and real-time replay.

View details
85
Repo Health
76
Technical
66
Dependency
Built with
TypeScript 64%
Rust 34%
Updated yesterday
Python
69%
MIT

Langflow

AI Agents · AI Development

155,451

Build, test, and deploy AI agents and RAG workflows visually with native API and MCP server export.

View details
90
Repo Health
85
Technical
65
Dependency
Built with
Python 69%
TypeScript 22%
Updated today
TypeScript
97%
Other

Langfuse

AI Development · Monitoring

35,581

Open source AI engineering platform for LLM observability, prompt management, evaluation, and debugging — self-host in minutes or use Langfuse Cloud.

View details
92
Repo Health
81
Technical
65
Dependency
Built with
TypeScript 97%
Updated today
TypeScript
78%
LGPL 3.0

Latitude

AI Agents · Monitoring

4,718

Open-source AI agent monitoring that catches what will break next before your users do.

View details
87
Repo Health
88
Technical
67
Dependency
Built with
TypeScript 78%
Python 11%
Rust 11%
Updated yesterday
Python
51%
AGPL 3.0

LearnHouse

CMS · Learning Management

2,331

Open-source LMS with AI tutoring, real-time collaboration boards, live code execution, and built-in course monetization — self-hosted in minutes.

View details
90
Repo Health
77
Technical
65
Dependency
Built with
Python 51%
TypeScript 47%
Updated today

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers