---
title: "What's New?"
description: "What changed in cva@1.0 since class-variance-authority@0.x, covering cva/config, cva/tailwindcss, composes, getSchema, internal variants, and breaking changes."
url: "https://cva.style/beta/getting-started/whats-new/"
---

What’s changed since `class-variance-authority@0.*`?

## Requirements

* [Install `cva@beta`](https://cva.style/beta/getting-started/installation).
* TypeScript 6.0 or later for TypeScript projects.
* Tailwind CSS v4 for the optional `cva/tailwindcss` stylesheet.

## Features

### 1. `class-variance-authority` → `cva`

One of the biggest (and let’s be honest, most important) complaints about `class-variance-authority` was that the name was just too damn long.

Shout-out to GitHub for transferring `npm` ownership of `cva`!

### 2. Bring your own concatenator with `cva/config`

`cva` and `cx` still use `clsx` by default. For your own setup, use [`defineConfig` from `cva/config`](https://cva.style/beta/api-reference#cvaconfig). Its required `cx` option accepts [`cn` or `tailwind-merge`](https://cva.style/beta/getting-started/installation#handling-class-conflicts), [`clsx/lite` for string-only classes](https://cva.style/beta/api-reference#advanced-concatenators), or your own concatenator.

#### `shadcn`

The [`shadcn` CLI](https://ui.shadcn.com/docs/registry/github) installs `cva@beta` with [`cn`](https://cva.style/beta/getting-started/installation#cn) and creates `cva.config.ts` in your project root:

```sh
pnpm dlx shadcn@latest add joe-bell/cva/cn
```

#### `cva/tailwindcss`

Import [`cva/tailwindcss`](https://cva.style/beta/getting-started/installation#cvatailwindcss) in a Tailwind CSS v4 project and prefix component defaults with `base:`. Ordinary utilities override them through the CSS cascade, with no runtime class merging.

### 3. Performance

Component calls are up to **1000%** faster than `class-variance-authority@0.7`. `cva` prepares class lookups when you create the component and runs your concatenator once per call:

* Components with variants and compound variants: **300% to 550%** faster per call.
* Matching a dozen compound variants: **700% to 1000%** faster.
* One component called with 24 different prop shapes: **400%** faster.
* Components with only a `base`: **50%** faster.

The `cva` entry is **1.64 KB** (brotli), measured from the current build.

Thanks to [@fveracoechea](https://github.com/fveracoechea) for inspiring this work! See the [runtime benchmarks](https://github.com/joe-bell/cva/pull/419) for the full results.

#### TypeScript

TypeScript does less work without changing inferred props, `getSchema` results, or emitted declarations. The [type-checking benchmarks](https://github.com/joe-bell/cva/pull/420) compare against the preceding beta revision using `tsc --extendedDiagnostics`:

* A project with 200 components type-checks about **10%** faster, with **26%** fewer type instantiations.
* Composed components: **16%** fewer instantiations. Components with a `getSchema` call: **24%** fewer.

### 4. Internal variants

Prefix a variant name with `_` to make it [internal](https://cva.style/beta/getting-started/variants#internal-variants). `VariantProps` and `getSchema` omit it, but you can still pass it directly to the `cva` component.

Breaking change

Rename any existing `_`-prefixed variant you expose as a public prop, or it will disappear from `VariantProps`.

### 5. `composes`

Shallow merge one or more `cva` components with the [`composes` property](https://cva.style/beta/getting-started/composing-components).

This [replaces the `compose` function](#6-compose--composes) from earlier betas.

### 6. `getSchema`

[`getSchema`](https://cva.style/beta/getting-started/tools#getschema) from `cva/tools` returns a plain object of variant names, values, and defaults. Use it to generate Storybook controls or variant galleries from your component.

### 7. Agent Skills

Two Agent Skills help your coding agent work with `cva`:

* [`cva-best-practices`](https://cva.style/beta/getting-started/skills#cva-best-practices) guides component authoring.
* [`cva-migrate`](https://cva.style/beta/getting-started/skills#cva-migrate) handles version upgrades.

### 8. Tailwind CSS IntelliSense

The [Tailwind CSS IntelliSense setup](https://cva.style/beta/getting-started/installation#intellisense) now uses `classFunctions` instead of `classRegex`. It covers Visual Studio Code, Zed, Neovim, and WebStorm, and works with `class-variance-authority` too.

## Breaking changes

### 1. `cva` now accepts a single parameter

Pass base styles in the config object’s `base` property:

```ts
import { cva } from "class-variance-authority";
import { cva } from "cva";


const component = cva("your-base-class");
const component = cva({ base: "your-base-class" });
```

The config object is required. Replace `cva()` with `cva({})` to avoid a TypeScript error.

### 2. Goodbye `null`

Passing `null` used to disable a variant, matching Stitches.js. This [caused a great deal of confusion](https://github.com/joe-bell/cva/discussions/97).

TypeScript now rejects `null` in props, `defaultVariants`, and `compoundVariants` selectors. At runtime, a `null` prop applies the variant’s default class instead of disabling it. Compound variants still compare the `null` as-is, so it isn’t interchangeable with `undefined`.

Use an explicit [`unset` variant](https://cva.style/beta/getting-started/variants#disabling-variants) instead:

```ts
const button = cva({
  base: "button",
  variants: { size: { small: "button--small" } },
  variants: { size: { unset: null, small: "button--small" } },
  defaultVariants: { size: "small" },
});


button({ size: null });
button({ size: "unset" });
// => "button"
```

### 3. Clearer type guards

`cva` infers variant types from your config. Passing a generic type argument now produces a TypeScript error.

### 4. Type imports move to the root

`cva` has no `types` entry point. Import `ClassValue`, `ClassArray`, and `ClassDictionary` from `cva` itself, alongside `VariantProps`.

Breaking change

* `class-variance-authority/types` has no equivalent. `ClassProp`, `ClassPropKey`, `OmitUndefined`, and `StringToBoolean` are no longer exported.
* `CxOptions` and `CxReturn` are now `CXOptions` and `CXReturn`.
* A variant named `__proto__` is rejected by the types.

```ts
import type { ClassValue } from "class-variance-authority/types";
import type { CxOptions, VariantProps } from "class-variance-authority";
import type { ClassValue, CXOptions, VariantProps } from "cva";
```

### 5. Configuration is read when you create the component

`cva` now reads `base`, `variants`, `compoundVariants`, `defaultVariants`, and `composes` when you create the component, which accounts for most of the [runtime speed improvement](#3-performance). Calls read your props and the current `cx` from `defineConfig`.

Breaking change

Treat the config and its referenced values as immutable after creation. Later mutations are unsupported and may only partly affect the output. Create a new component instead:

```ts
const config = {
  base: "button",
  variants: { intent: { primary: "bg-blue-500" } },
};
const button = cva(config);


config.variants.intent.primary = "bg-indigo-500";
const button = cva({
  base: "button",
  variants: { intent: { primary: "bg-blue-500" } },
});


const indigoButton = cva({
  base: "button",
  variants: { intent: { primary: "bg-indigo-500" } },
});
```

Recreate the parent after adding to a `composes` array or changing a child’s config. Neither change updates the parent’s merged variants.

`component.config` exposes fresh objects for the merged `variants` and `defaultVariants`. This internal property is safe to read, but mutating it is unsupported and may affect later output. Frozen configs work, and `cva` never mutates or freezes your inputs.

### 6. `compose` → `composes`

This change only affects earlier `cva` betas. `class-variance-authority` never had `compose`.

`cva@1.0` removes the deprecated `compose` function. Use `composes` inside `cva` instead, passing a single component or an array:

```ts
import { cva, compose } from "cva";
import { cva } from "cva";


const box = cva({ /* ... */ });
const root = cva({ /* ... */ });


const card = compose(box, root);
const card = cva({ composes: [box, root] });
```

Note

Conflicting `defaultVariants` now use the [last declared default for every component](https://cva.style/beta/getting-started/composing-components#extending-variants). `compose` let each component resolve its own default.

## Migrating to 1.0

Use the [`cva-migrate` Agent Skill](https://cva.style/beta/getting-started/skills#cva-migrate) to upgrade from `class-variance-authority@0.x` or an earlier `cva` beta.
