---
title: "Installation"
description: "Install class-variance-authority and configure Tailwind CSS IntelliSense and style-conflict handling."
url: "https://cva.style/getting-started/installation/"
---

* pnpm

  ```sh
  pnpm i class-variance-authority
  ```

* npm

  ```sh
  npm i class-variance-authority
  ```

* yarn

  ```sh
  yarn add class-variance-authority
  ```

* bun

  ```sh
  bun add class-variance-authority
  ```

* deno

  ```sh
  deno add class-variance-authority
  ```

Do I have to write such a long package name?

Unfortunately, for a little bit longer, yes. Originally, the plan was to publish the package as `cva`, but someone had already registered that name on npm and [marked it as a “placeholder”](https://www.npmjs.com/package/cva).

On 2022/02/16, GitHub transferred NPM ownership of `cva` to [Joe Bell](https://joebell.studio/). This shorter name will be used from v1 onwards.

In the meantime, you can always alias the package for your convenience…

1. Alias the package with [`npm install`](https://docs.npmjs.com/cli/v6/commands/npm-install)

   ```sh
   npm i cva@npm:class-variance-authority
   ```

2. Then import like so:

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


   // …
   ```

## Tailwind CSS

If you’re a Tailwind user, here are some additional (optional) steps to get the most out of `cva`:

### IntelliSense

You can enable autocompletion inside `cva` using the steps below:

* Visual Studio Code

  1. [Install the “Tailwind CSS IntelliSense” Visual Studio Code extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)

  2. Add the following to your [`settings.json`](https://code.visualstudio.com/docs/getstarted/settings):

  ```json
  {
    "tailwindCSS.classFunctions": ["cva", "cx"]
  }
  ```

* Zed

  Add the following to your [`.zed/settings.json`](https://zed.dev/docs/configuring-zed#settings-files):

  ```json
  {
    "lsp": {
      "tailwindcss-language-server": {
        "settings": {
          "classFunctions": ["cva", "cx"],
        }
      }
    }
  }
  ```

* Neovim

  1. [Install the extension](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss)

  2. Add the following configuration:

  ```lua
  require 'lspconfig'.tailwindcss.setup({
    settings = {
      tailwindCSS = {
        classFunctions = { "cva", "cx" },
      },
    },
  })
  ```

* WebStorm

  1. Check the version. Available for [WebStorm 2023.1](https://www.jetbrains.com/webstorm/whatsnew/#version-2023-1-tailwind-css-configuration) and later

  2. Open the settings. Go to [Languages and Frameworks | Style Sheets | Tailwind CSS](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_configuration)

  3. Add the following to your tailwind configuration

  ```json
  {
    "classFunctions": ["cva", "cx"]
  }
  ```

### Handling style conflicts

By default, `cva` joins class names but does not resolve conflicting Tailwind CSS utilities. Choose one of the approaches below:

#### CSS defaults with `base:`

This Tailwind CSS v4 recipe adds a `base:` variant that marks a utility as a component default, so any ordinary utility overrides it through the CSS cascade instead of runtime class merging. Add it after Tailwind’s import:

```css
@import "tailwindcss";


@custom-variant base {
  @layer base {
    :where(&) {
      @slot;
    }
  }
}
```

Prefix your defaults with `base:` and write state styles as ordinary utilities:

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


const buttonVariants = cva(
  "base:bg-blue-600 base:px-4 base:py-2 base:text-white hover:bg-blue-700",
);


buttonVariants({ className: "bg-violet-600" });
```

In this example, `bg-violet-600` wins over `base:bg-blue-600`, and `hover:bg-blue-700` still applies on hover.

`base:` utilities sit in a nested layer below ordinary utilities but above Tailwind’s `base` and `components` layers. Inspired by [Diego Haz’s `base:` idea](https://x.com/diegohaz/status/1897776774206853269) and [a related `:where()` example](https://x.com/acd02/status/1897943176863756335).

`base:` makes defaults overridable. It does not merge classes, so keep these rules in mind:

* **Conditional defaults lose too**: any ordinary `bg-*` utility beats `base:hover:bg-*`, `base:sm:bg-*`, and `base:dark:bg-*`, even while the condition matches. Write state styles without `base:`.
* **Class order still doesn’t matter**: two ordinary utilities that conflict, or two `base:` utilities that conflict, resolve by stylesheet order.
* **Put `base:` before pseudo-element variants**: `base:before:bg-*` works, but `before:base:bg-*` compiles to an invalid selector and never applies.
* **`!important` flips the layer order**: `base:bg-blue-600!` beats `bg-violet-600!`.

#### tailwind-merge

If you’d rather not think about the cascade, [`tailwind-merge`](https://github.com/dcastil/tailwind-merge) is more bulletproof: it merges classes at runtime, so the last conflicting class wins. Wrap your `cva` component with `twMerge`:

Example with tailwind-merge

```ts
import { cva, type VariantProps } from "class-variance-authority";
import { twMerge } from "tailwind-merge";


const buttonVariants = cva(["your", "base", "classes"], {
  variants: {
    intent: {
      primary: ["your", "primary", "classes"],
    },
  },
  defaultVariants: {
    intent: "primary",
  },
});


export interface ButtonVariants extends VariantProps<typeof buttonVariants> {}


export const button = (variants: ButtonVariants) =>
  twMerge(buttonVariants(variants));
```
