# Technical Documentation: unovue/shadcn-vue

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/unovue/shadcn-vue)

### Overview (`README.md`)

# shadcn-vue

A set of beautifully designed components that you can customize, extend, and build on. Start here then make it your own. Open Source. Open Code. **Use this to build your own component library**.



## Documentation

Visit http://shadcn-vue.com/docs to view the documentation.

## Contributing

Please read the [contributing guide](/CONTRIBUTING.md).

## License

Licensed under the [MIT license](https://github.com/unovue/shadcn-vue/blob/dev/LICENSE).

---

### Overview (`apps/v4/README.md`)

# Nuxt Minimal Starter

Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.

## Setup

Make sure to install dependencies:

```bash
# npm
npm install

# pnpm
pnpm install

# yarn
yarn install

# bun
bun install
```

## Development Server

Start the development server on `http://localhost:3000`:

```bash
# npm
npm run dev

# pnpm
pnpm dev

# yarn
yarn dev

# bun
bun run dev
```

## Production

Build the application for production:

```bash
# npm
npm run build

# pnpm
pnpm build

# yarn
yarn build

# bun
bun run build
```

Locally preview production build:

```bash
# npm
npm run preview

# pnpm
pnpm preview

# yarn
yarn preview

# bun
bun run preview
```

Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

---

### Overview (`packages/cli/README.md`)

# shadcn-vue

A CLI for adding components to your project.

## Usage

### create

Use the `create` command to scaffold a new Vue project with shadcn-vue pre-configured.

```bash
npx shadcn-vue create
```

The `create` command will:
- Create a new Vue project (Nuxt, Vite, or Vite + Vue Router)
- Configure Tailwind CSS
- Set up shadcn-vue with your chosen design system

#### Options

| Option | Description |
|--------|-------------|
| `-t, --template <template>` | Framework template: `nuxt`, `vite`, or `vite-router` |
| `-p, --preset <preset>` | Use a preset: `reka-vega`, `reka-nova`, `reka-maia`, `reka-lyra`, `reka-mira` |
| `--base <base>` | Component library: `reka` |
| `--style <style>` | Visual style: `vega`, `nova`, `maia`, `lyra`, `mira` |
| `--icon-library <lib>` | Icon library: `lucide`, `tabler`, `hugeicons`, `phosphor`, `remixicon` |
| `--font <font>` | Font: `inter`, `figtree`, `jetbrains-mono`, `geist`, `geist-mono` |
| `-b, --base-color <color>` | Base color: `neutral`, `gray`, `zinc`, `stone`, `slate` |
| `-y, --yes` | Skip confirmation prompts |

#### Examples

```bash
# Interactive mode
npx shadcn-vue create

# Use a preset
npx shadcn-vue create --preset reka-nova

# Full customization
npx shadcn-vue create --template nuxt --style maia --icon-library hugeicons --font figtree
```

### init

Use the `init` command to initialize dependencies for an existing project.

The `init` command installs dependencies, adds the `cn` util, configures `tailwind.config.js`, and CSS variables for the project.

```bash
npx shadcn-vue init
```

#### Options

| Option | Description |
|--------|-------------|
| `--base <base>` | Component library: `reka` |
| `--style <style>` | Visual style: `vega`, `nova`, `maia`, `lyra`, `mira` |
| `--icon-library <lib>` | Icon library: `lucide`, `tabler`, `hugeicons`, `phosphor`, `remixicon` |
| `--font <font>` | Font: `inter`, `figtree`, `jetbrains-mono`, `geist`, `geist-mono` |
| `-b, --base-color <color>` | Base color: `neutral`, `gray`, `zinc`, `stone`, `slate` |
| `-d, --defaults` | Use default configuration |
| `-y, --yes` | Skip confirmation prompts |

### add

Use the `add` command to add components to your project.

The `add` command adds a component to your project and installs all required dependencies.

```bash
npx shadcn-vue add [component]
```

#### Example

```bash
npx shadcn-vue add alert-dialog
```

You can also run the command without any arguments to view a list of all available components:

```bash
npx shadcn-vue add
```

## Visual Styles

shadcn-vue supports 5 visual styles that transform how components look:

| Style | Description |
|-------|-------------|
| **Vega** | The classic shadcn/ui look. Clean, neutral, and familiar. |
| **Nova** | Reduced padding and margins for compact layouts. |
| **Maia** | Soft and rounded, with generous spacing. |
| **Lyra** | Boxy and sharp. Pairs well with mono fonts. |
| **Mira** | Compact. Made for dense interfaces. |

## Presets

Presets are pre-configured combinations of style, icons, and fonts:

| Preset | Style | Icons | Font |
|--------|-------|-------|------|
| `reka-vega` | Vega | Lucide | Inter |
| `reka-nova` | Nova | Hugeicons | Inter |
| `reka-maia` | Maia | Hugeicons | Figtree |
| `reka-lyra` | Lyra | Hugeicons | JetBrains Mono |
| `reka-mira` | Mira | Hugeicons | Inter |

## Documentation

Visit http://shadcn-vue.com to view the documentation.

## License

Licensed under the [MIT license](https://github.com/shadcn/ui/blob/main/LICENSE.md).

---

### Overview (`packages/module/README.md`)

# Shadcn Nuxt

[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![License][license-src]][license-href]
[![Nuxt][nuxt-src]][nuxt-href]

Shadcn Vue module for Nuxt.

- [✨ &nbsp;Release Notes](/CHANGELOG.md)

- [📖 &nbsp;Documentation](https://www.shadcn-vue.com/docs/installation/nuxt.html)

## Features


- ⛰ Auto-import correct and relevant components
- more to come...

## Quick Setup

1. Add `shadcn-nuxt` dependency to your project

```bash
# Using pnpm
pnpm add -D shadcn-nuxt

# Using yarn
yarn add --dev shadcn-nuxt

# Using npm
npm install --save-dev shadcn-nuxt
```

2. Add `shadcn-nuxt` to the `modules` section of `nuxt.config.ts`

```js
export default defineNuxtConfig({
  modules: [
    'shadcn-nuxt'
  ],
  shadcn: {
    /**
     * Prefix for all the imported component
     */
    prefix: '',
    /**
     * Directory that the component lives in.
     * @default "./components/ui"
     */
    componentDir: './components/ui'
  }
})
```

That's it! You can now use Shadcn Nuxt in your Nuxt app ✨

### Multiple component directories

Projects that split their base UI components from custom extensions can configure multiple directories, each with its own optional prefix:

```ts
export default defineNuxtConfig({
  modules: ['shadcn-nuxt'],
  shadcn: {
    prefix: 'Ui',
    componentDir: [
      '@/components/ui',
      {
        path: '@/components/ai',
        prefix: 'Ai',
      },
    ],
  },
})
```

Each directory is ignored by Nuxt's default auto-import scanning and re-registered through the module, ensuring clean separation without console warnings. See [issue #1593](https://github.com/unovue/shadcn-vue/issues/1593) for the background.

## Development

```bash
# Install dependencies
npm install

# Generate type stubs
npm run dev:prepare

# Develop with the playground
npm run dev

# Build the playground
npm run dev:build

# Run ESLint
npm run lint

# Run Vitest
npm run test
npm run test:watch

# Release new version
npm run release
```


[npm-version-src]: https://img.shields.io/npm/v/shadcn-nuxt/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
[npm-version-href]: https://npmjs.com/package/shadcn-nuxt

[npm-downloads-src]: https://img.shields.io/npm/dm/shadcn-nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D
[npm-downloads-href]: https://npmjs.com/package/shadcn-nuxt

[license-src]: https://img.shields.io/npm/l/shadcn-nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D
[license-href]: https://npmjs.com/package/shadcn-nuxt

[nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
[nuxt-href]: https://nuxt.com

---

### Overview (`deprecated/www/__registry__/README.md`)

> Files inside this directory is autogenerated by `./scripts/build-registry.ts`. **Do not edit them manually.**

---

### Apps V4 Content Docs Directory Index (`apps/v4/content/docs/directory/index.md`)

---
title: Registry Directory
description: Discover community registries for shadcn-vue components and blocks.
navigation:
  title: Directory
---

These registries are built into the CLI with no additional configuration required. To add a component, run: `npx shadcn-vue@latest add @<registry>/<component>`.

::callout{class="bg-muted font-semibold"}
Community registries are maintained by third-party developers. Always review code on installation to ensure it meets your security and quality standards.
::

Don't see a registry? Learn how to [add it here](/docs/registry/getting-started).

::registry-directory
::

## Documentation

You can use the `shadcn-vue` CLI to run your own code registry. Running your own registry allows you to distribute your custom components, hooks, pages, config, rules and other files to any project.

<div class="mt-6 grid gap-4 sm:grid-cols-2">
  <linked-card href="/docs/registry/getting-started" class="items-start text-sm md:p-6">
    <div class="font-medium">Getting Started</div>
    <div class="text-muted-foreground">Set up and build your own registry</div>
  </linked-card>
  <linked-card href="/docs/registry/examples" class="items-start text-sm md:p-6">
    <div class="font-medium">Examples</div>
    <div class="text-muted-foreground">Registry item examples and configurations</div>
  </linked-card>
  <linked-card href="/docs/registry/registry-json" class="items-start text-sm md:p-6">
    <div class="font-medium">registry.json</div>
    <div class="text-muted-foreground">Schema specification for registry.json</div>
  </linked-card>
  <linked-card href="/docs/registry/registry-item-json" class="items-start text-sm md:p-6">
    <div class="font-medium">registry-item.json</div>
    <div class="text-muted-foreground">Schema specification for registry items</div>
  </linked-card>
  <linked-card href="/docs/registry/faq" class="items-start text-sm md:p-6">
    <div class="font-medium">FAQ</div>
    <div class="text-muted-foreground">Frequently asked questions</div>
  </linked-card>
</div>

---

### Apps V4 Content Docs Registry Getting Started (`apps/v4/content/docs/registry/getting-started.md`)

---
title: Getting Started
description: Learn how to get setup and run your own component registry.
---

This guide will walk you through the process of setting up your own component registry.

It assumes you already have a project with components and would like to turn it into a registry.



## registry.json

The `registry.json` file is only required if you're using the `shadcn-vue` CLI to build your registry.

If you're using a different build system, you can skip this step as long as your build system produces valid JSON files that conform to the [registry-item schema specification](/docs/registry/registry-item-json).

<Steps>

### Add a registry.json file

Create a `registry.json` file in the root of your project. Your project can be a Nuxt, Vite, or any other project that supports Vue.

```json showLineNumbers title="registry.json"
{
  "$schema": "https://shadcn-vue.com/schema/registry.json",
  "name": "acme",
  "homepage": "https://acme.com",
  "items": [
    // ...
  ]
}
```

This `registry.json` file must conform to the [registry schema specification](/docs/registry/registry-json).

</Steps>

## Add a registry item

<Steps>

### Create your component

Add your first component. Here's an example of a simple `<HelloWorld />` component:

```vue showLineNumbers title="registry/new-york/HelloWorld/HelloWorld.vue"
<script setup lang="ts">
import { Button } from "@/components/ui/button"
</script>

<template>
  <Button>Hello World</Button>
</template>
```

<Callout class="mt-6">

  **Note:** This example places the component in the `registry/new-york`
  directory. You can place it anywhere in your project as long as you set the
  correct path in the `registry.json` file and you follow the `registry/[NAME]`
  directory structure.

</Callout>

```txt
registry
└── new-york
    └── HelloWorld
        └── HelloWorld.vue
```

<Callout class="mt-6 [&_pre]:mb-0">

  **Important:** If you're placing your component in a custom directory, make
  sure it is configured in your `tailwind.config.ts` file.

```ts showLineNumbers
// tailwind.config.ts
export default {
  content: ["./registry/**/*.{js,ts,jsx,tsx,vue}"],
}
```

</Callout>

### Add your component to the registry

To add your component to the registry, you need to add your component definition to `registry.json`.

```json showLineNumbers title="registry.json"  {6-17}
{
  "$schema": "https://shadcn-vue.com/schema/registry.json",
  "name": "acme",
  "homepage": "https://acme.com",
  "items": [
    {
      "name": "hello-world",
      "type": "registry:block",
      "title": "Hello World",
      "description": "A simple hello world component.",
      "files": [
        {
          "path": "registry/new-york/HelloWorld/HelloWorld.vue",
          "type": "registry:component"
        }
      ]
    }
  ]
}
```

You define your registry item by adding a `name`, `type`, `title`, `description` and `files`.

For every file you add, you must specify the `path` and `type` of the file. The `path` is the relative path to the file from the root of your project. The `type` is the type of the file.

You can read more about the registry item schema and file types in the [registry item schema docs](/docs/registry/registry-item-json).

</Steps>

## Build your registry

<Steps>

### Install the shadcn-vue CLI

Note: the `build` command is currently only available in the `shadcn-vue@canary` version of the CLI.

```bash
npm install shadcn-vue@latest
```

### Add a build script

Add a `registry:build` script to your `package.json` file.

```json showLineNumbers title="package.json"
{
  "scripts": {
    "registry:build": "shadcn-vue build"
  }
}
```

### Run the build script

Run the build script to generate the registry JSON files.

```bash
npm run registry:build
```

<Callout class="mt-6">

**Note:** By default, the build script will generate the registry JSON files in `public/r` e.g `public/r/hello-world.json`.

You can change the output directory by passing the `--output` option. See the [shadcn-vue build command](/docs/cli#build) for more information.

</Callout>

</Steps>

## Serve your registry

If you're running your registry on Nuxt, you can now serve your registry by running the `nuxt` server. The command might differ for other frameworks.

```bash
npm run dev
```

Your files will now be served at `http://localhost:3000/r/[NAME].json` eg. `http://localhost:3000/r/hello-world.json`.

## Publish your registry

To make your registry available to other developers, you can publish it by deploying your project to a public URL.

## Adding Auth

The `shadcn-vue` CLI does not offer a built-in way to add auth to your registry. We recommend handling authorization on your registry server.

A common simple approach is to use a `token` query parameter to authenticate requests to your registry. e.g. `http://localhost:3000/r/hello-world.json?token=[SECURE_TOKEN_HERE]`.

Use the secure token to authenticate requests and return a 401 Unauthorized response if the token is invalid. Both the `shadcn` CLI and `Open in v0` will handle the 401 response and display a message to the user.

<Callout class="mt-6">
  **Note:** Make sure to encrypt and expire tokens.
</Callout>

## Guidelines

Here are some guidelines to follow when building components for a registry.

- Place your registry item in the `registry/[STYLE]/[NAME]` directory. I'm using `new-york` as an example. It can be anything you want as long as it's nested under the `registry` directory.
- The following properties are required for the block definition: `name`, `description`, `type` and `files`.
- Make sure to list all registry dependencies in `registryDependencies`. A registry dependency is the name of the component in the registry eg. `input`, `button`, `card`, etc or a URL to a registry item eg. `http://localhost:3000/r/editor.json`.
- Make sure to list all dependencies in `dependencies`. A dependency is the name of the package in the registry eg. `zod`, `sonner`, etc. To set a version, you can use the `name@version` format eg. `zod@^3.20.0`.
- **Imports should always use the `@/registry` path.** eg. `import { HelloWorld } from "@/registry/new-york/hello-world/hello-world"`
- Ideally, place your files within a registry item in `components`, `hooks`, `lib` directories.

## Install using the CLI

To install a registry item using the `shadcn-vue` CLI, use the `add` command followed by the URL of the registry item.

```bash
npx shadcn-vue@latest add http://localhost:3000/r/hello-world.json
```

## Install from GitHub

If your registry lives in a **public** GitHub repository with a `registry.json`
at its root, you do not have to build, host or configure anything. The CLI can
read it directly:

```bash
npx shadcn-vue@latest add owner/repo/hello-world
```

The CLI resolves the repository's default branch, reads `registry.json` from the
root, finds the item by name, and fetches each of its `files[].path` from the
same commit. Every file of an item comes from one commit, so a branch that moves
mid-install cannot give you a half-updated component.

### Pinning a branch, tag or commit

Append `#ref` to install from somewhere other than the default branch:

```bash
npx shadcn-vue@latest add owner/repo/hello-world#main
npx shadcn-vue@latest add owner/repo/hello-world#v1.2.0
npx shadcn-vue@latest add owner/repo/hello-world#1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b
```

Branches win over tags when a name is ambiguous, and an annotated tag resolves to
the commit it points at.

### Listing what a repository publishes

```bash
npx shadcn-vue@latest search owner/repo
```

### Only install from repositories you trust

Adding a registry item runs third-party code on your machine. This is true of
every registry — hosted, local or GitHub — but the GitHub form is worth calling
out because it needs no configuration, so a single command from a README is
enough to install from a repository you have never looked at.

A registry item can list npm `dependencies`, which the CLI installs with your
package manager, which in turn runs that package's install scripts. It can also
write files into your project. Treat `npx shadcn-vue@latest add owner/repo/item`
with the same care as `npm install owner-repo` — read the registry first if you
do not know who owns it.

The CLI does reject `path` and `target` values that are absolute or use `..` to
escape the project, both for the item you asked for and at the point files are
written. That is a guard against mistakes and a hostile item's easiest trick; it
is not a sandbox.

### Notes

- **Item names may contain slashes.** `owner/repo/forms/login` installs the item
  *named* `forms/login` from the root `registry.json`. It is not a path to a
  nested `registry.json`.
- **The repository must be public.** `raw.githubusercontent.com` does not serve
  private repositories. Use a hosted registry with [auth](#adding-auth) instead.
- **Git is required.** The CLI shells out to `git ls-remote` to resolve the ref,
  which is what lets it find the default branch instead of guessing `main`.
- **`registryDependencies` are resolved the usual way.** A full URL is fetched
  as-is; a bare name such as `button` resolves against the default `shadcn-vue`
  registry, exactly as it does for any other registry item. Note that a
  dependency can point anywhere, so trusting a repository means trusting what it
  depends on too.

If you would rather not type the repository on every command, register a
namespace in `components.json` and install by that instead. A namespace maps to
a hosted registry URL template — not to a GitHub repository — so this is an
alternative to the GitHub form above rather than a shorthand for it:

```json title="components.json"
{
  "registries": {
    "@acme": "https://acme.com/r/{name}.json"
  }
}
```

```bash
npx shadcn-vue@latest add @acme/hello-world
```

---

### Apps V4 Content Docs Registry Index (`apps/v4/content/docs/registry/index.md`)

---
title: Registry
description: Run your own component registry.
---

<Callout class="mt-0" >

  **Note:** This feature is currently experimental. Help us improve it by
  testing it out and sending feedback. If you have any questions, please [reach
  out to us](https://github.com/unovue/shadcn-vue/discussions).

</Callout>

You can use the `shadcn-vue` CLI to run your own component registry. Running your own registry allows you to distribute your custom components, hooks, pages, and other files to any Vue project.

Registry items are automatically compatible with the `shadcn-vue` CLI.

## Requirements

You are free to design and host your custom registry as you see fit. The only requirement is that your registry items must be valid JSON files that conform to the [registry-item schema specification](/docs/registry/registry-item-json).

---

### Deprecated Www Src Content Docs Registry Getting Started (`deprecated/www/src/content/docs/registry/getting-started.md`)

---
title: Getting Started
description: Learn how to get setup and run your own component registry.
---

This guide will walk you through the process of setting up your own component registry.

It assumes you already have a project with components and would like to turn it into a registry.



## registry.json

The `registry.json` file is only required if you're using the `shadcn-vue` CLI to build your registry.

If you're using a different build system, you can skip this step as long as your build system produces valid JSON files that conform to the [registry-item schema specification](/docs/registry/registry-item-json).

<Steps>

### Add a registry.json file

Create a `registry.json` file in the root of your project. Your project can be a Nuxt, Vite, or any other project that supports Vue.

```json:line-numbers title="registry.json"
{
  "$schema": "https://shadcn-vue.com/schema/registry.json",
  "name": "acme",
  "homepage": "https://acme.com",
  "items": [
    // ...
  ]
}
```

This `registry.json` file must conform to the [registry schema specification](/docs/registry/registry-json).

</Steps>

## Add a registry item

<Steps>

### Create your component

Add your first component. Here's an example of a simple `<HelloWorld />` component:

```vue:line-numbers title="registry/new-york/HelloWorld/HelloWorld.vue"
<script setup lang="ts">
import { Button } from "@/components/ui/button"
</script>

<template>
  <Button>Hello World</Button>
</template>
```

<Callout class="mt-6">

  **Note:** This example places the component in the `registry/new-york`
  directory. You can place it anywhere in your project as long as you set the
  correct path in the `registry.json` file and you follow the `registry/[NAME]`
  directory structure.

</Callout>

```txt
registry
└── new-york
    └── HelloWorld
        └── HelloWorld.vue
```

<Callout class="mt-6 [&_pre]:mb-0">

  **Important:** If you're placing your component in a custom directory, make
  sure it is configured in your `tailwind.config.ts` file.

```ts:line-numbers
// tailwind.config.ts
export default {
  content: ["./registry/**/*.{js,ts,jsx,tsx,vue}"],
}
```

</Callout>

### Add your component to the registry

To add your component to the registry, you need to add your component definition to `registry.json`.

```json:line-numbers title="registry.json"  {6-17}
{
  "$schema": "https://shadcn-vue.com/schema/registry.json",
  "name": "acme",
  "homepage": "https://acme.com",
  "items": [
    {
      "name": "hello-world",
      "type": "registry:block",
      "title": "Hello World",
      "description": "A simple hello world component.",
      "files": [
        {
          "path": "registry/new-york/HelloWorld/HelloWorld.vue",
          "type": "registry:component"
        }
      ]
    }
  ]
}
```

You define your registry item by adding a `name`, `type`, `title`, `description` and `files`.

For every file you add, you must specify the `path` and `type` of the file. The `path` is the relative path to the file from the root of your project. The `type` is the type of the file.

You can read more about the registry item schema and file types in the [registry item schema docs](/docs/registry/registry-item-json).

</Steps>

## Build your registry

<Steps>

### Install the shadcn-vue CLI

Note: the `build` command is currently only available in the `shadcn-vue@canary` version of the CLI.

```bash
npm install shadcn-vue@latest
```

### Add a build script

Add a `registry:build` script to your `package.json` file.

```json:line-numbers title="package.json"
{
  "scripts": {
    "registry:build": "shadcn-vue build"
  }
}
```

### Run the build script

Run the build script to generate the registry JSON files.

```bash
npm run registry:build
```

<Callout class="mt-6">

**Note:** By default, the build script will generate the registry JSON files in `public/r` e.g `public/r/hello-world.json`.

You can change the output directory by passing the `--output` option. See the [shadcn build command](/docs/cli#build) for more information.

</Callout>

</Steps>

## Serve your registry

If you're running your registry on Nuxt, you can now serve your registry by running the `nuxt` server. The command might differ for other frameworks.

```bash
npm run dev
```

Your files will now be served at `http://localhost:3000/r/[NAME].json` eg. `http://localhost:3000/r/hello-world.json`.

## Publish your registry

To make your registry available to other developers, you can publish it by deploying your project to a public URL.

## Adding Auth

The `shadcn-vue` CLI does not offer a built-in way to add auth to your registry. We recommend handling authorization on your registry server.

A common simple approach is to use a `token` query parameter to authenticate requests to your registry. e.g. `http://localhost:3000/r/hello-world.json?token=[SECURE_TOKEN_HERE]`.

Use the secure token to authenticate requests and return a 401 Unauthorized response if the token is invalid. Both the `shadcn` CLI and `Open in v0` will handle the 401 response and display a message to the user.

<Callout class="mt-6">
  **Note:** Make sure to encrypt and expire tokens.
</Callout>

## Guidelines

Here are some guidelines to follow when building components for a registry.

- Place your registry item in the `registry/[STYLE]/[NAME]` directory. I'm using `new-york` as an example. It can be anything you want as long as it's nested under the `registry` directory.
- The following properties are required for the block definition: `name`, `description`, `type` and `files`.
- Make sure to list all registry dependencies in `registryDependencies`. A registry dependency is the name of the component in the registry eg. `input`, `button`, `card`, etc or a URL to a registry item eg. `http://localhost:3000/r/editor.json`.
- Make sure to list all dependencies in `dependencies`. A dependency is the name of the package in the registry eg. `zod`, `sonner`, etc. To set a version, you can use the `name@version` format eg. `zod@^3.20.0`.
- **Imports should always use the `@/registry` path.** eg. `import { HelloWorld } from "@/registry/new-york/hello-world/hello-world"`
- Ideally, place your files within a registry item in `components`, `hooks`, `lib` directories.

## Install using the CLI

To install a registry item using the `shadcn-vue` CLI, use the `add` command followed by the URL of the registry item.

```bash
npx shadcn-vue@latest add http://localhost:3000/r/hello-world.json
```

---

### Deprecated Www Src Content Docs Registry Index (`deprecated/www/src/content/docs/registry/index.md`)

---
title: Registry
description: Run your own component registry.
---

<Callout class="mt-0" >

  **Note:** This feature is currently experimental. Help us improve it by
  testing it out and sending feedback. If you have any questions, please [reach
  out to us](https://github.com/unovue/shadcn-vue/discussions).

</Callout>

You can use the `shadcn-vue` CLI to run your own component registry. Running your own registry allows you to distribute your custom components, hooks, pages, and other files to any Vue project.

Registry items are automatically compatible with the `shadcn-vue` CLI.

## Requirements

You are free to design and host your custom registry as you see fit. The only requirement is that your registry items must be valid JSON files that conform to the [registry-item schema specification](/docs/registry/registry-item-json).

---

### Superpowers Specs 2026 07 11 Reka Rhea Style Design (`docs/superpowers/specs/2026-07-11-reka-rhea-style-design.md`)

# reka-rhea style — design spec

**Date:** 2026-07-11
**Status:** Approved, ready for implementation

## Goal

Add a new style, `rhea`, to shadcn-vue that faithfully mirrors shadcn-ui React's
`base-rhea` style, so users can pick the look that is closest to upstream
shadcn/ui. The style must be fully integrated: authored token map, generated
per-style components, published registry JSON, runtime-scoped preview, and
selectable in the customizer/style switcher.

## Background: how styles work in this repo

A "style" is a single file — `apps/v4/registry/styles/style-{name}.css` — that
maps `cn-*` placeholder tokens (authored in `apps/v4/registry/bases/reka/ui/**`,
the single source-of-truth component set) to expanded Tailwind utility classes,
scoped under a `.style-{name} { ... }` wrapper.

Each `style-{name}.css` serves **two** purposes:

1. **Build time.** `scripts/lib/build-styles.ts` (`buildStyles()`) reads the
   file, builds a token→utilities map (`createStyleMap`), and transforms every
   file in `registry/bases/reka/ui/**` via `vue-metamorph`, replacing `cn-*`
   tokens with their expansions. Output → `apps/v4/styles/reka-{name}/ui/**`
   (a fully-overwritten build artifact). `buildStylesRegistry()` then publishes
   `apps/v4/public/r/styles/reka-{name}/*.json` for CLI installs.
   Both loop over the `STYLES` array and run inside `pnpm registry:build`.
2. **Runtime.** `assets/css/main.css` `@import`s each `style-{name}.css` under
   `layer(base)`, so `.style-{name}`-wrapped DOM renders that style live — this
   powers the customizer preview.

Token-expansion rules (`expand-cn-classes.ts`):
- A `cn-*` token present in the map is replaced by its utilities (merged via
  `tailwind-merge`).
- A `cn-*` token **absent** from the map is **stripped** (unless allowlisted:
  `cn-menu-target`, `cn-menu-translucent`, `cn-logical-sides`, `cn-rtl-flip`,
  `cn-font-heading` — these stay as literal classes, defined globally).

`STYLES` (`registry/styles.ts`) supplies switcher metadata (name/title/
description/icon). `PRESETS` (`registry/config.ts`) supplies a
base+style+theme+font bundle that the `PresetPicker`/`Customizer` render.

## Source of truth

The React repo already ships the exact token map:
`/Users/zernonia/Desktop/Coding/shadcn-ui/apps/v4/registry/styles/style-rhea.css`
(1550 lines, `.style-rhea {}` wrapper). This Vue pipeline is a deliberate
mirror of React's build-registry flow, so the file adapts near-verbatim.

### Token-alignment analysis (verified)

- Vue base references **388** unique `cn-*` tokens; the style-variant subset
  that shipping styles (e.g. vega) actually define is **367**.
- React `style-rhea.css` defines **373** tokens and is a **superset** of vega's
  367 — every style-variant token the Vue base needs has a rhea value.
- **React-extra tokens** (~31: `cn-drawer-popup`, `cn-select-value`, various
  `*-logical` direction variants, etc.) that the Vue base never references:
  harmless, remain unused in the map. Kept for future-proofing.
- **Vue-base tokens with cross-repo name drift** (drawer / combobox /
  pagination / navigation-menu families) that React's map does not name
  identically: these get **stripped** on expansion — which is exactly what
  every existing Vue style (vega, nova, …) already does. Those components fall
  back to their inline base classes, so `rhea` is no less complete than the
  shipping styles. Full per-token reconciliation was explicitly deferred
  (chosen approach: near-verbatim + verify).

## Approach: near-verbatim port + verify

Copy React's `style-rhea.css` essentially as-is (keeping the `.style-rhea`
wrapper and all rules), then wire the 4 integration points and run the build.
Verify by rendering, not by hand-diffing 1550 lines.

## Changes

### 1. `apps/v4/registry/styles/style-rhea.css` (new)
Adapted from React `style-rhea.css`. `.style-rhea { … }` wrapper preserved.
No content edits beyond what verification surfaces (e.g. any Vue-base token that
must be added because the Vue base — unlike React's — genuinely requires a
per-style value; expected: none, confirmed at verify step).

### 2. `apps/v4/registry/styles.ts` (edit)
Append a `STYLES` entry:
```ts
{
  name: "rhea",
  title: "Rhea",
  description: "The latest shadcn/ui look. Refined spacing and modern defaults.",
  icon: `<svg … 24×24 …>` // authored to match the existing icon set's visual language; user may swap
}
```

### 3. `apps/v4/registry/config.ts` (edit)
Append a `PRESETS` entry mirroring React's `base-rhea` preset:
```ts
{
  name: "reka-rhea",
  title: "Rhea",
  description: "Rhea / Lucide / Inter",
  base: "reka",
  style: "rhea",
  baseColor: "neutral",
  theme: "neutral",
  chartColor: "neutral",
  iconLibrary: "lucide",
  font: "inter",
  fontHeading: "inherit",
  item: "Item",
  menuAccent: "subtle",
  menuColor: "default",
  radius: "default",
}
```

### 4. `apps/v4/assets/css/main.css` (edit)
Add alongside the other style imports:
```css
@import "../../registry/styles/style-rhea.css" layer(base);
```

### 5. Generated artifacts (run the build)
Run `pnpm registry:build` (or the standalone `buildStyles()` +
`buildStylesRegistry()` runners) to produce:
- `apps/v4/styles/reka-rhea/ui/**` (66 components + composables)
- `apps/v4/public/r/styles/reka-rhea/*.json`
These are build outputs, committed like the other `reka-*` style outputs.

## Out of scope (explicit)

- **Default style unchanged.** `DEFAULT_CONFIG.style` stays `nova`. `rhea` is
  additive/selectable.
- **Homepage cards repoint** (`@/styles/reka-nova/*` → `@/styles/reka-rhea/*`)
  is an **optional follow-up**, not part of this task. Can be done after `rhea`
  is verified if the landing page should visually match upstream shadcn/ui.
- **Full token reconciliation** of drift-named edge components — deferred.

## Verification plan

1. `buildStyles()` runs clean; grep generated `styles/reka-rhea/ui/**` for
   leftover unexpanded `cn-*` tokens (expect only allowlisted ones, matching
   other styles).
2. `eslint` the generated output (the build already runs `eslint --fix`).
3. Boot dev server; render a `.style-rhea`-wrapped preview (or temporarily
   point a card demo at `@/styles/reka-rhea/*`) and visually confirm parity
   with React `base-rhea`.
4. Confirm `Rhea` appears in the style switcher (PresetPicker) and selecting it
   renders correctly.
5. Confirm `public/r/styles/reka-rhea/*.json` generated for CLI installs.

## Success criteria

- `styles/reka-rhea/ui/**` generated, compiles, no stray `cn-*`.
- `Rhea` selectable in the customizer and renders faithfully to base-rhea.
- Registry JSON published.
- No regression to existing styles (shared base + pipeline untouched except the
  additive `STYLES`/`PRESETS`/`main.css`/`style-rhea.css` entries).

---

### Superpowers Specs 2026 07 11 Unifont Font Loading Design (`docs/superpowers/specs/2026-07-11-unifont-font-loading-design.md`)

# Replace `@nuxt/fonts` with unifont (client-side font loading)

**Date:** 2026-07-11
**Scope:** `apps/v4` docs/customizer app only. Not the CLI-facing font registry.

## Problem

The v4 app used the `@nuxt/fonts` module to auto-download `@font-face` files by
scanning CSS for font-family names. That module is now disabled (commented out in
`nuxt.config.ts`, not in the `modules` array), which means:

- **Docs have no real Geist**: the font vars name `"Geist"` but nothing downloads
  the `@font-face`, so text falls back to Arial.
- **The customizer's font pickers don't work end-to-end**: `FontPicker.vue` +
  `useDesignSystemProvider.ts` swap the `--font-sans` / `--font-heading` CSS vars
  to a family string, but nothing loads the actual `@font-face` for the ~25 fonts
  in `lib/fonts.ts` when one is picked.

We want to control font downloading **programmatically** with `unifont` instead of
the auto-magic module: load only **Geist** by default (for docs), and fetch other
fonts on demand when the user selects them in the customizer.

## Decisions

| Decision | Choice | Rationale |
|---|---|---|
| Where unifont runs | **Client-side (browser)** | User preference; no server route needed. |
| Provider | **`bunny`** (`unifont` `providers.bunny`) | Only provider whose endpoints (`/list`, `/css`) are fully CORS-enabled for browser `fetch`. Google's `fonts.google.com/metadata/fonts` returns **no** `access-control-allow-origin`, so unifont's `google` provider fails in the browser. Bunny mirrors Google Fonts with **identical family names**, so `lib/fonts.ts` names map 1:1 with zero remapping. Verified Bunny carries Geist, Geist Mono, and all 25 fonts. |
| Default Geist loading | **Eager Bunny `<link>`** in `nuxt.config` head | No JS dependency, fast LCP, no flash of fallback for docs. |
| unifont bundle inclusion | **Dynamic `import('unifont')`** inside the composable | `unifont` pulls in `css-tree` (non-trivial). Lazy-loading keeps docs/marketing pages that never open the customizer lean. |

### CORS verification (empirical, `curl -I -H "Origin: …"`)

- `fonts.google.com/metadata/fonts` → 200, **no** `access-control-allow-origin` → browser-blocked.
- `fonts.googleapis.com/css2` → `access-control-allow-origin: *` (but the google provider still needs the metadata endpoint above).
- `fonts.bunny.net/list` and `/css` → `access-control-allow-origin: *` → works client-side. ✅

## Architecture

Three independent parts.

### 1. Default Geist for docs — eager, no JS

- Re-enable `@import "./fonts.css"` in `apps/v4/assets/css/main.css` (currently
  commented out) and keep the `@theme` font vars as-is.
- Add a plain Bunny stylesheet link to `app.head.link` in
  `apps/v4/nuxt.config.ts`:
  - `https://fonts.bunny.net/css?family=geist:400,500,600,700|geist-mono:400,500`
  - Include `<link rel="preconnect" href="https://fonts.bunny.net">` for connection warm-up.
- Result: every page renders Geist immediately with no JS dependency.

### 2. On-demand fonts in the customizer — unifont + Bunny (client)

New composable `apps/v4/composables/useFontLoader.ts`:

- **Memoized unifont instance**: a module-level singleton promise created via a
  dynamic `import('unifont')` →
  `createUnifont([providers.bunny()])`. Created lazily on first `loadFont` call.
- **`loadFont(family: string): Promise<void>`**:
  - No-op on the server (`import.meta.client` guard) and for empty/`inherit` values.
  - Skip families already covered by the eager default (`Geist`, `Geist Mono`).
  - Dedupe via a module-level `Set<string>` keyed by family (also guard against
    concurrent in-flight loads keyed the same way) so re-selecting a font never
    refetches or re-injects.
  - `resolveFont(family, { weights: ['400','500','600','700'] })` (Bunny returns
    only the weights it actually has).
  - Build `@font-face` CSS from the returned `FontFaceData[]` (family, weight,
    style, `font-display: swap`, `src` list from `RemoteFontSource`/`LocalFontSource`,
    `unicode-range`).
  - Inject a single `<style data-font="<family>">` element into `document.head`.
  - Wrap in try/catch → on failure `console.warn` and resolve anyway
    (non-blocking; the CSS var is still set and the family falls back through the
    existing CSS font stack).

### 3. Wire into the design-system provider

In `apps/v4/composables/useDesignSystemProvider.ts`, the existing watcher applies
`selectedFont` / `selectedHeadingFont` by setting `--font-sans` / `--font-heading`.
Call `loadFont(font.name)` for the relevant selected font(s) in that same watcher,
right before/as the CSS var is set. This single integration point covers the body,
heading, and mono pickers (all flow through this provider). Loading is fire-and-forget
(non-blocking); `font-display: swap` handles the visual swap when the file arrives.

### 4. Tailwind v4 wiring (make the font actually apply)

Fonts must render through TW4's `@theme` system, not as loose CSS vars. The chain:

```
main.css  @theme inline { --font-sans: var(--font-sans); ... }
   → generates utilities:  .font-sans { font-family: var(--font-sans) }
   → these reference the runtime-overridable custom property --font-sans

fonts.css  @theme { --font-sans: "Geist", ...; --font-heading: ...; --font-mono: ... }
   → emits the DEFAULT --font-sans/-heading/-mono onto :root
```

Because `main.css` uses **`@theme inline`**, Tailwind does **not** emit a
`--font-sans` value itself — it only inlines `var(--font-sans)` into the utilities
and expects the custom property to exist. That property is provided by the
**non-inline `@theme`** in `fonts.css`.

**Critical fix:** `@import "./fonts.css"` is currently **commented out** in
`main.css`. While commented, `var(--font-sans)` has no default value, so every
`font-sans` / `font-heading` / `font-mono` utility resolves to nothing until the
customizer's JS runs. **Re-enabling the import** (Part 1) is what makes Geist apply
correctly through TW4 by default.

**`fonts.css` must use `:root`, NOT `@theme`.** The `@theme inline` block in
`main.css` already registers the `font-sans` / `font-heading` / `font-mono` theme
keys (mapping the utilities to `var(--font-*)`). If `fonts.css` also declares those
keys via `@theme`, the two collide: the `inline` block wins and emits a
**self-referential** `--font-sans: var(--font-sans)` into `@layer theme`, which is
cyclic and resolves to nothing — every font var (and `--default-font-family`)
breaks. Providing the concrete defaults as a plain **unlayered `:root { --font-sans:
… }`** block avoids the collision and, being unlayered, wins over the layered
theme output. This mirrors shadcn's own color pattern (`@theme inline {
--color-background: var(--background) }` + `:root { --background: … }`). Runtime
overrides (`documentElement.style.setProperty('--font-sans', …)`, inline style) sit
above `:root` and still win.

Requirements for correctness:

- Re-enable `@import "./fonts.css"` so the default `--font-*` custom properties
  exist on `:root`. Leave the `main.css` `@theme inline` block unchanged — its
  `var(--font-*)` mapping is what lets both the CSS default and the runtime JS
  override (`documentElement.style.setProperty('--font-sans', …)`) flow into the
  `font-sans` / `font-heading` / `font-mono` utilities.
- **Family-name match**: the `@font-face` family that `useFontLoader` injects must
  exactly equal the primary family in the string `useDesignSystemProvider` writes
  to `--font-sans` (e.g. `lib/fonts.ts` `fontFamily: "'Inter', sans-serif"` →
  injected `@font-face { font-family: 'Inter' }`). Bunny returns the canonical
  Google family name, which matches `lib/fonts.ts` names, so this holds — but the
  builder must not rename the family.
- The eager Bunny `<link>` (Part 1) provides the actual `@font-face` for the
  `Geist` / `Geist Mono` families that the `--font-sans` / `--font-mono` defaults
  reference by name, and `.theme-mono` (`--font-sans: var(--font-mono)`) inherits
  correctly.

**Flagged, related pre-existing bug (decide separately):** `themes.css` has
font-theme presets `.theme-inter`, `.theme-noto-sans`, `.theme-nunito-sans`,
`.theme-figtree` that set `--font-sans: var(--font-inter)` etc., but the
`--font-inter` / `--font-noto-sans` / … custom properties are **defined nowhere**
(they have no fallback), so these presets silently render an invalid font-family.
This is independent of the customizer path (which writes the family string directly).
**Not fixed in this change** unless requested; recommended follow-up is to either
delete these dead presets or route them through `useFontLoader` + real values.

### 5. Remove `@nuxt/fonts`

- Delete the commented `fonts: { … }` config block in `nuxt.config.ts`.
- Remove `"@nuxt/fonts"` from `apps/v4/package.json` dependencies.

## Data flow

```
FontPicker sets param
  → useDesignSystemSearchParams updates font / fontHeading
    → useDesignSystemProvider watcher runs
      → useFontLoader.loadFont(family)   (dynamic import unifont, bunny resolveFont, inject @font-face)
      → sets --font-sans / --font-heading CSS var
        → text swaps to loaded font (font-display: swap)
```

## Error handling

- unifont / Bunny fetch failure → caught, `console.warn`, non-blocking. The CSS var
  is still set; text uses the fallback in the existing font stack.
- Dedupe `Set` + in-flight guard prevents duplicate fetches / duplicate `<style>`.
- SSR: `loadFont` is a no-op on the server; injection happens client-only.

## Out of scope

- `apps/v4/registry/fonts.ts` — CLI-facing registry for consumers of the
  `shadcn-vue` CLI; separate concern, untouched.
- The in-progress theme-color edits currently in the working tree
  (`main.css` oklch tweaks) — unrelated, left alone.
- Self-hosting fonts / build-time generation — not needed; Bunny CDN is used at
  runtime.

## Testing / verification

- **TW4 default wiring**: with `fonts.css` re-imported, inspect a `font-sans`
  element → computed `font-family` resolves to `"Geist", …` (not empty / Arial-only)
  with **no** customizer JS run. Confirms `@theme inline` → `var(--font-sans)` →
  `fonts.css @theme` default chain is intact.
- **Docs baseline**: load a docs page (customizer never opened) → Network shows the
  eager Bunny Geist request; body renders in Geist; `unifont`/`css-tree` **not** in
  the loaded JS.
- **Customizer**: open the customizer, pick `Inter` → Network shows a
  `fonts.bunny.net` request; a `<style data-font="Inter">` appears in `<head>`;
  preview text renders in Inter. Re-selecting Inter → no new request. Pick a heading
  font and a mono font → same behavior.
- **Failure path**: simulate offline / block bunny.net → no crash; text falls back;
  a single warning is logged.
```

---

### Apps V4 Content Docs .About (`apps/v4/content/docs/.about.md`)

---
title: About
description: Powered by amazing open source projects.
---

## About

[shadcn-vue](https://shadcn-vue.com) is a port of [shadcn/ui](https://ui.shadcn.com) for Vue/Nuxt. It's maintained by [unovue](https://github.com/unovue).

## Credits

- [shadcn](https://twitter.com/shadcn) - The brilliant mind behind the designs, methodology, and implementation of the original [shadcn/ui](https://ui.shadcn.com).
- [Reka UI](https://reka-ui.com) - The headless components that power this project.
- [Radix UI](https://radix-ui.com) - The headless components and examples that the original [shadcn/ui](https://ui.shadcn.com) was built on.
- [Shu Ding](https://shud.in) - The typography style is adapted from his work on Nextra.
- [Cal](https://cal.com) - Where shad copied the styles for the first component: the `Button`.

## License

MIT © [shadcn](https://shadcn.com) & [unovue](https://github.com/unovue)

---

### Apps V4 Content Docs .Charts (`apps/v4/content/docs/.charts.md`)

---
title: Charts
description: Versatile visualization tool, allowing users to represent data using various types of charts for effective analysis.
label: Legacy
---

<script setup>
import Area from '~icons/gravity-ui/chart-area-stacked'
import Bar from '~icons/gravity-ui/chart-column'
import Line from '~icons/gravity-ui/chart-line'
import Pie from '~icons/gravity-ui/chart-pie'
</script>

<Callout>

**Legacy**: Component will be moved to [extended repo](https://github.com/unovue/shadcn-vue/issues/1077) with Tailwind v4 support.

---

Only works with Vue >3.3

</Callout>

`Charts` components were built on top of [Unovis](https://unovis.dev/) (a modular data visualization framework), and inspired by [tremor](https://www.tremor.so).

## Chart type

<div class="grid gap-4 mt-8 sm:grid-cols-2 sm:gap-6 not-docs">
  <LinkedCard href="/docs/charts/area">
    <Area class="text-foreground/80 w-11 h-11" />
    <p class="mt-2 font-medium">Area</p>
  </LinkedCard>

  <LinkedCard href="/docs/charts/line">
    <Line class="text-foreground/80 w-11 h-11" />
    <p class="mt-2 font-medium">Line</p>
  </LinkedCard>

  <LinkedCard href="/docs/charts/bar">
    <Bar class="text-foreground/80 w-11 h-11" />
    <p class="mt-2 font-medium">Bar</p>
  </LinkedCard>

  <LinkedCard href="/docs/charts/donut">
    <Pie class="text-foreground/80 w-11 h-11" />
    <p class="mt-2 font-medium">Donut</p>
  </LinkedCard>
</div>

## Installation

<Steps>

### Update `css`

Add the following tooltip styling to your `tailwind.css` file:

```css
@layer base {
  :root {
    /* ... */
    --vis-tooltip-background-color: none !important;
    --vis-tooltip-border-color: none !important;
    --vis-tooltip-text-color: none !important;
    --vis-tooltip-shadow-color: none !important;
    --vis-tooltip-backdrop-filter: none !important;
    --vis-tooltip-padding: none !important;

    --vis-primary-color: var(--primary);
    /* change to any hsl value you want */
    --vis-secondary-color: 160 81% 40%;
    --vis-text-color: var(--muted-foreground);
  }
}
```

If you are not using `css-variables` for your component, you need to update the `--vis-primary-color` and `--vis-text-color` to your desired hsl value.

You may use [this tool](https://redpixelthemes.com/blog/tailwindcss-colors-different-formats/) to help you find the hsl value for your primary color and text color. Be sure to provide `dark` mode styling as well.

</Steps>

## Colors

By default, we construct the primary theme color, and secondary (`--vis-secondary-color`) color with different opacity for the graph.

However, you can always pass in the desired `color` into each chart.

```vue
<template>
  <AreaChart
    :data="data"
    :colors="['blue', 'pink', 'orange', 'red']"
  />
</template>
```

## Custom tooltip

If you want to customize the `Tooltip` for the chart, you can pass `customTooltip` prop with a custom Vue component.
The custom component would receive `title` and `data` props, check out [ChartTooltip.vue component](https://github.com/unovue/shadcn-vue/tree/dev/apps/www/registry/default/ui/chart/ChartTooltip.vue) for example.

The expected prop definition would be:

```ts
defineProps<{
  title?: string
  data: {
    name: string
    color: string
    value: any
  }[]
}>()
```

---

### Apps V4 Content Docs .Contribution (`apps/v4/content/docs/.contribution.md`)

---
title: Contribution
description: Learn on how to contribute to shadcn/vue.
---
<script setup lang="ts">
import { Button } from "@/registry/new-york-v4/ui/button"

const latestSyncCommitTag = "06cc0cdf3d080555d26abbe6639f2d7f6341ec73"

const latestSyncCommitUrl = `https://github.com/shadcn-ui/ui/commit/${latestSyncCommitTag}`
const diffUrl = `https://github.com/shadcn-ui/ui/compare/${latestSyncCommitTag}...main`
</script>

## Introduction

Thanks for your interest in contributing to shadcn-vue.com. We're happy to have you here.

Please take a moment to review this document before submitting your first pull request. We also strongly recommend that you check for open issues and pull requests to see if someone else is working on something similar.

If you need any help, feel free to reach out to the core team on [Discord](https://chat.unovue.com/).

This guide provides detailed information to help new contributors.

## About this repository

This repository is a monorepo.

- We use [pnpm](https://pnpm.io) and [`workspaces`](https://pnpm.io/workspaces) for development.

## Project Structure

The GitHub repository consists of the several folders. here's a quick view.



1. **packages** -
Contains source code for supporting `nuxt` as a module and the `cli` to add new components.

2. **apps/www** -
The main folder that holds the source code for the website and every `shadcn/vue` component. This folder contains important sub-folders and is a subproject with its own `package.json`.

3. **.vitepress** -
Contains the configuration and source code for `vitepress` and the `shadcn/vue` website.

4. **src** -
Hosts the main source code for every `shadcn/vue` component or demo and their documentation on the website.

5. **\_\_registry\_\_** -
Holds the registry file generated by `scripts/build-registry.ts` to serve components for the `cli`. This folder's content is auto-generated and should not be edited manually.

6. **scripts** -
Contains various helper scripts, such as `build-registry.ts`, which automatically generates the `__registry__` folder.

7. **content** -
This folder holds all the documentation for the `/docs` route. Each component has one `.md` file documenting the installation and usage of the component.

8. **examples** -
Holds all examples not part of `/docs`, like the [main page](/).

9. **registry** -
The main folder hosts the source code for different styles of every component. This is likely the main folder you'll be changing.

We support two different styles for every component in `shadcn/vue`:

1. Default
2. New York

Every component added to the repository must support both versions, including the main source code and associated demos.

When adding or modifying components, please ensure that:

1. You make the changes for every style.
2. You update the documentation.
3. You run `pnpm build:registry` to update the registry.

## Development

Start by cloning the repository:

```bash
git clone git@github.com:unovue/shadcn-vue.git
```

### Install dependencies

```bash
pnpm install
```

### Run a workspace

You can use the `pnpm --filter=[WORKSPACE]` command to start the development process for a workspace or some of the shortcut command that we have setup.

#### Examples

1. To run the `shadcn-vue.com` website:

```bash
pnpm dev
```

2. To run the `shadcn-vue` cli package:

```bash
pnpm dev:cli
```

## Documentation

The documentation for this project is located in the `www` workspace. You can run the documentation locally by running the following command:

```bash
pnpm dev
```

Documentation is written using [md](https://vitepress.dev/guide/markdown). You can find the documentation files in the `apps/www/src/content` directory.

## CLI

The `shadcn-vue` package is a CLI for adding components to your project. You can find the documentation for the CLI [here](https://shadcn-vue.com/docs/cli).

Any changes to the CLI should be made in the `packages/cli` directory. If you can, it would be great if you could add tests for your changes.

## Testing

Tests are written using [Vitest](https://vitest.dev). You can run all the tests from the root of the repository.

```bash
pnpm test
```

Please ensure that the tests are passing when submitting a pull request. If you're adding new features, please include tests.

## Commit Convention

Before you create a Pull Request, please check whether your commits comply with
the commit conventions used in this repository.

When you create a commit we kindly ask you to follow the convention
`category(scope or module): message` in your commit message while using one of
the following categories:

- `feat / feature`: all changes that introduce completely new code or new
  features
- `fix`: changes that fix a bug (ideally you will additionally reference an
  issue if present)
- `refactor`: any code related change that is not a fix nor a feature
- `docs`: changing existing or creating new documentation (i.e. README, docs for
  usage of a lib or cli usage)
- `build`: all changes regarding the build of the software, changes to
  dependencies or the addition of new dependencies
- `test`: all changes regarding tests (adding new tests or changing existing
  ones)
- `ci`: all changes regarding the configuration of continuous integration (i.e.
  github actions, ci system)
- `chore`: all changes to the repository that do not fit into any of the above
  categories

  e.g. `feat(components): add new prop to the avatar component`

If you are interested in the detailed specification you can visit [Conventional Commits](https://www.conventionalcommits.org/).

## SFC - Single File Components

Multiple components are integrated into one file in `shadcn/ui` - the React version of `shadcn` - while Vue only supports one component per file, hence the name Single File Component (SFC). In such cases, you need to create separate files for each component part and then export them all in an `index.ts` file.

See the [`Accordion`](https://github.com/unovue/shadcn-vue/tree/v0.10.2/apps/www/registry/default/ui/accordion) source code as an example.

## Wrapping Reka UI Components

[Reka UI](https://www.reka-ui.com) hosts many low-level UI components that are used to build reusable components.
There are many cases that you need to wrap `Reka UI` components.

### Props & Events
All of the `Reka UI` components expose their prop and emit types. We need to forward any props/events that are coming from outside to the `Reka UI` component.

To do so, we have a helper function named [`useForwardPropsEmits`](https://www.reka-ui.com/docs/utilities/use-forward-props-emits.html) that combines props and events that must be binded to the child radix component.

To be more clear, the function `useForwardPropsEmits` takes in props and an optional emit function, and returns a
computed object that combines the parsed props and emits as props.

Here's an example from `Accordion` root component.

```vue
<script setup lang="ts">
import {
  AccordionRoot,
  type AccordionRootEmits,
  type AccordionRootProps,
  useForwardPropsEmits,
} from 'reka-ui'

const props = defineProps<AccordionRootProps>()
const emits = defineEmits<AccordionRootEmits>()

const forwarded = useForwardPropsEmits(props, emits)
</script>

<template>
  <AccordionRoot v-bind="forwarded">
    <slot />
  </AccordionRoot>
</template>
```

As you can see, `AccordionRootEmits` and `AccordionRootProps` types are imported from radix, combined with `useForwardPropsEmits` and then are binded using `v-bind` syntax.

### CSS Classes
There are cases when we want to accept `class` as a prop in our `shadcn/vue` component and then combine it with a default tailwind class on our `Reka UI` component via `cn` utility function.

In these cases, we can not use `v-bind`, because this would lead in [double class binding](https://github.com/unovue/shadcn-vue/pull/241).

Take a look at `DrawerDescription.vue`.

```vue
<script lang="ts" setup>
import type { DrawerDescriptionProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { DrawerDescription } from 'reka-ui'
import { cn } from '@/lib/utils'

const props = defineProps<DrawerDescriptionProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')
</script>

<template>
  <DrawerDescription
    data-slot="drawer-description"
    v-bind="delegatedProps"
    :class="cn('text-muted-foreground text-sm', props.class)"
  >
    <slot />
  </DrawerDescription>
</template>
```

As you can see, we have created a computed property named `delegatedProps` to remove `class` from props, and only then bind
the returned value to our radix component (`DrawerDescription` in this case).

As for our class, we first declared it as type of `HTMLAttributes['class']` and used `cn` to merge tailwind classes from `class` prop and our own classes.

This pattern only needs to be applied when the `cn` utility is required. For instances where there are no default Tailwind classes that need to be merged with user-provided classes, this pattern is not necessary. A good example of this is the `SelectValue.vue` component.

```vue
<script setup lang="ts">
import { SelectValue, type SelectValueProps } from 'reka-ui'

const props = defineProps<SelectValueProps>()
</script>

<template>
  <SelectValue v-bind="props">
    <slot />
  </SelectValue>
</template>
```

### Boolean Props
When you are building a wrapper for a component, in some cases you want to ignore Vue [Props Boolean Casting](https://vuejs.org/guide/components/props.html#boolean-casting).
You can either set default value as undefined for all the boolean field, or you can use [`useForwardProps`](https://www.reka-ui.com/docs/utilities/use-forward-props.html) composable.

Take a look at `AccordionItem.vue`

```vue
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { AccordionItem, type AccordionItemProps, useForwardProps } from 'reka-ui'
import { cn } from '@/lib/utils'

const props = defineProps<AccordionItemProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <AccordionItem
    v-bind="forwardedProps"
    :class="cn('border-b', props.class)"
  >
    <slot />
  </AccordionItem>
</template>
```

Since `AccordionItemProps` type has atleast one boolean property, we need to use `useForwardProps` on the entire props object.

Note that `useForwardPropsEmits` use `useForwardProps` under the hood.

### Component as Root
Whenever your root component is a `Component` Primitive from vue, it's easier to use [`Primitive`](https://www.reka-ui.com/docs/utilities/primitive.html) instead.

Let's take a look at `Button.vue`

```vue
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { Primitive, type PrimitiveProps } from 'reka-ui'
import { cn } from '@/lib/utils'
import { type ButtonVariants, buttonVariants } from '.'

interface Props extends PrimitiveProps {
  variant?: ButtonVariants['variant']
  size?: ButtonVariants['size']
  class?: HTMLAttributes['class']
}

const props = withDefaults(defineProps<Props>(), {
  as: 'button',
})
</script>

<template>
  <Primitive
    :as="as"
    :as-child="asChild"
    :class="cn(buttonVariants({ variant, size }), props.class)"
  >
    <slot />
  </Primitive>
</template>
```

You'll need to extend `PrimitiveProps` in your props to support `Primitive` component. In most cases you would also need a default value for [`as`](https://www.reka-ui.com/docs/utilities/primitive.html#changing-as-value) property.

## Updating with `shadcn/ui`

`shadcn/vue` is an unofficial, community-led Vue port of `shadcn/ui`, as time goes by, they might get out of sync.

As of today, we are in sync with this <a :href="latestSyncCommitUrl" target="_blank">commit</a> of `shadcn/ui`.

Click on the following link to check if there are newer commits that we should be synced with.

<div class="text-center">
  <a :href="diffUrl" target="_blank">
    <Button>
      Check Diff
    </Button>
  </a>
</div>

1. There are no changes - If you see "There isn’t anything to compare", nothing needs to be done as we are synced with latest version.
2. If there are changes, you should review thoese changes and try to apply them on `shadcn/vue` codebase and create a PR, remember to update the `latestSyncCommitTag` in [this file](https://github.com/unovue/shadcn-vue/blob/dev/apps/www/src/content/docs/contribution.md) too.

## Debugging
Here are some tools and techniques that can help you debug more effectively while contributing to `shadcn/vue` or developing your own projects.

### Install Vue Dev Tools
To easily inspect component props, attributes, events, and more, you can leverage the  [`Vue DevTools`](https://devtools.vuejs.org/)  extension for browsers. This extension provides a user-friendly interface for debugging Vue components and can improve your development experience.

### Enable Custom Formmaters
Vue wraps values stored in a `ref` in a way that, when logged, results in a nested object and requires manual inspection to access the value stored in the ref.

You can enable Custom Formatters in your browser to automate this process.

- [Firefox](https://firefox-source-docs.mozilla.org/devtools-user/custom_formatters/index.html)
- Chrome, Edge, Brave and other Chromium based [browsers](https://www.google.com/search?q=how+to+enable+custom++formatter+chrome)

---

### Apps V4 Content Docs .Tailwind V4 (`apps/v4/content/docs/.tailwind-v4.md`)

---
title: Tailwind v4
description: How to use shadcn-vue with Tailwind v4.
---

<script setup>
import Button from '@/registry/new-york-v4/ui/button/Button.vue'
</script>

It's here! Tailwind v4. Ready for you to try out. You can start using it today.

<div class="flex gap-2 items-center mt-6 not-docs">
  <Button asChild size="sm" class="rounded-lg">
    <a href="#try-it-out">Get Started</a>
  </Button>
  <Button asChild size="sm" variant="outline" class="rounded-lg">
    <a href="https://v4.shadcn-vue.com" target="_blank" rel="noopener noreferrer">
      See Demo
    </a>
  </Button>
</div>

## What's New

- The CLI can now initialize projects with Tailwind v4.
- Full support for the new `@theme` directive and `@theme inline` option.
- All components are updated for Tailwind v4.
- Every primitive now has a `data-slot` attribute for styling.
- We've fixed and cleaned up the style of the components.
- We're deprecating the `toast` component in favor of `sonner`.
- Buttons now use the default cursor.
- We're deprecating the `default` style. New projects will use `new-york`.
- HSL colors are now converted to OKLCH.

**Note: this is non-breaking. Your existing apps with Tailwind v3 will still work. When you add new components, they'll still be in v3 until you upgrade. Only new projects start with Tailwind v4.**

## See it Live

I put together a demo with all the updated components here: https://v4.shadcn-vue.com

Take a look and test the components. If you find any bugs, please let me know on [GitHub](https://github.com/unovue/shadcn-vue).

## Try It Out

See the framework specific guides below for how to get started.

<div class="grid gap-4 mt-8 sm:grid-cols-2 sm:gap-6 not-docs">
  <LinkedCard href="/docs/installation/vite">
    <svg
      role="img"
      viewBox="0 0 24 24"
      xmlns="http://www.w3.org/2000/svg"
      class="w-10 h-10"
      fill="currentColor"
    >
      <title>Vite</title>
      <path d="m8.286 10.578.512-8.657a.306.306 0 0 1 .247-.282L17.377.006a.306.306 0 0 1 .353.385l-1.558 5.403a.306.306 0 0 0 .352.385l2.388-.46a.306.306 0 0 1 .332.438l-6.79 13.55-.123.19a.294.294 0 0 1-.252.14c-.177 0-.35-.152-.305-.369l1.095-5.301a.306.306 0 0 0-.388-.355l-1.433.435a.306.306 0 0 1-.389-.354l.69-3.375a.306.306 0 0 0-.37-.36l-2.32.536a.306.306 0 0 1-.374-.316zm14.976-7.926L17.284 3.74l-.544 1.887 2.077-.4a.8.8 0 0 1 .84.369.8.8 0 0 1 .034.783L12.9 19.93l-.013.025-.015.023-.122.19a.801.801 0 0 1-.672.37.826.826 0 0 1-.634-.302.8.8 0 0 1-.16-.67l1.029-4.981-1.12.34a.81.81 0 0 1-.86-.262.802.802 0 0 1-.165-.67l.63-3.08-2.027.468a.808.808 0 0 1-.768-.233.81.81 0 0 1-.217-.6l.389-6.57-7.44-1.33a.612.612 0 0 0-.64.906L11.58 23.691a.612.612 0 0 0 1.066-.004l11.26-20.135a.612.612 0 0 0-.644-.9z" />
    </svg>
    <p class="mt-2 font-medium">Vite</p>
  </LinkedCard>
  <LinkedCard href="/docs/installation/nuxt">
    <svg xmlns="http://www.w3.org/2000/svg"  class="w-12 h-12" viewBox="0 0 900 900" fill="none">
    <title>Nuxt</title>
    <path d="M504.908 750H839.476C850.103 750.001 860.542 747.229 869.745 741.963C878.948 736.696 886.589 729.121 891.9 719.999C897.211 710.876 900.005 700.529 900 689.997C899.995 679.465 897.193 669.12 891.873 660.002L667.187 274.289C661.876 265.169 654.237 257.595 645.036 252.329C635.835 247.064 625.398 244.291 614.773 244.291C604.149 244.291 593.711 247.064 584.511 252.329C575.31 257.595 567.67 265.169 562.36 274.289L504.908 372.979L392.581 179.993C387.266 170.874 379.623 163.301 370.42 158.036C361.216 152.772 350.777 150 340.151 150C329.525 150 319.086 152.772 309.883 158.036C300.679 163.301 293.036 170.874 287.721 179.993L8.12649 660.002C2.80743 669.12 0.00462935 679.465 5.72978e-06 689.997C-0.00461789 700.529 2.78909 710.876 8.10015 719.999C13.4112 729.121 21.0523 736.696 30.255 741.963C39.4576 747.229 49.8973 750.001 60.524 750H270.538C353.748 750 415.112 713.775 457.336 643.101L559.849 467.145L614.757 372.979L779.547 655.834H559.849L504.908 750ZM267.114 655.737L120.551 655.704L340.249 278.586L449.87 467.145L376.474 593.175C348.433 639.03 316.577 655.737 267.114 655.737Z" fill="currentColor"/>
    </svg>
    <p class="mt-2 font-medium">Nuxt</p>
  </LinkedCard>
  <LinkedCard href="/docs/installation/astro">
    <svg
      role="img"
      viewBox="0 0 24 24"
      xmlns="http://www.w3.org/2000/svg"
      class="w-10 h-10"
      fill="currentColor"
    >
      <title>Astro</title>
      <path
        d="M16.074 16.86C15.354 17.476 13.917 17.895 12.262 17.895C10.23 17.895 8.527 17.263 8.075 16.412C7.914 16.9 7.877 17.458 7.877 17.814C7.877 17.814 7.771 19.564 8.988 20.782C8.988 20.15 9.501 19.637 10.133 19.637C11.216 19.637 11.215 20.582 11.214 21.349V21.418C11.214 22.582 11.925 23.579 12.937 24C12.7812 23.6794 12.7005 23.3275 12.701 22.971C12.701 21.861 13.353 21.448 14.111 20.968C14.713 20.585 15.383 20.161 15.844 19.308C16.0926 18.8493 16.2225 18.3357 16.222 17.814C16.2221 17.4903 16.1722 17.1685 16.074 16.86ZM15.551 0.6C15.747 0.844 15.847 1.172 16.047 1.829L20.415 16.176C18.7743 15.3246 17.0134 14.7284 15.193 14.408L12.35 4.8C12.3273 4.72337 12.2803 4.65616 12.2162 4.60844C12.152 4.56072 12.0742 4.53505 11.9943 4.53528C11.9143 4.5355 11.8366 4.56161 11.7727 4.60969C11.7089 4.65777 11.6623 4.72524 11.64 4.802L8.83 14.405C7.00149 14.724 5.23264 15.3213 3.585 16.176L7.974 1.827C8.174 1.171 8.274 0.843 8.471 0.6C8.64406 0.385433 8.86922 0.218799 9.125 0.116C9.415 0 9.757 0 10.443 0H13.578C14.264 0 14.608 0 14.898 0.117C15.1529 0.219851 15.3783 0.386105 15.551 0.6Z"
        fill="currentColor"
      />
    </svg>
    <p class="mt-2 font-medium">Astro</p>
  </LinkedCard>
  <LinkedCard href="/docs/installation/laravel">
    <svg
      role="img"
      viewBox="0 0 62 65"
      fill="currentColor"
      xmlns="http://www.w3.org/2000/svg"
      class="w-10 h-10"
    >
      <path d="M61.8548 14.6253C61.8778 14.7102 61.8895 14.7978 61.8897 14.8858V28.5615C61.8898 28.737 61.8434 28.9095 61.7554 29.0614C61.6675 29.2132 61.5409 29.3392 61.3887 29.4265L49.9104 36.0351V49.1337C49.9104 49.4902 49.7209 49.8192 49.4118 49.9987L25.4519 63.7916C25.3971 63.8227 25.3372 63.8427 25.2774 63.8639C25.255 63.8714 25.2338 63.8851 25.2101 63.8913C25.0426 63.9354 24.8666 63.9354 24.6991 63.8913C24.6716 63.8838 24.6467 63.8689 24.6205 63.8589C24.5657 63.8389 24.5084 63.8215 24.456 63.7916L0.501061 49.9987C0.348882 49.9113 0.222437 49.7853 0.134469 49.6334C0.0465019 49.4816 0.000120578 49.3092 0 49.1337L0 8.10652C0 8.01678 0.0124642 7.92953 0.0348998 7.84477C0.0423783 7.8161 0.0598282 7.78993 0.0697995 7.76126C0.0884958 7.70891 0.105946 7.65531 0.133367 7.6067C0.152063 7.5743 0.179485 7.54812 0.20192 7.51821C0.230588 7.47832 0.256763 7.43719 0.290416 7.40229C0.319084 7.37362 0.356476 7.35243 0.388883 7.32751C0.425029 7.29759 0.457436 7.26518 0.498568 7.2415L12.4779 0.345059C12.6296 0.257786 12.8015 0.211853 12.9765 0.211853C13.1515 0.211853 13.3234 0.257786 13.475 0.345059L25.4531 7.2415H25.4556C25.4955 7.26643 25.5292 7.29759 25.5653 7.32626C25.5977 7.35119 25.6339 7.37362 25.6625 7.40104C25.6974 7.43719 25.7224 7.47832 25.7523 7.51821C25.7735 7.54812 25.8021 7.5743 25.8196 7.6067C25.8483 7.65656 25.8645 7.70891 25.8844 7.76126C25.8944 7.78993 25.9118 7.8161 25.9193 7.84602C25.9423 7.93096 25.954 8.01853 25.9542 8.10652V33.7317L35.9355 27.9844V14.8846C35.9355 14.7973 35.948 14.7088 35.9704 14.6253C35.9792 14.5954 35.9954 14.5692 36.0053 14.5405C36.0253 14.4882 36.0427 14.4346 36.0702 14.386C36.0888 14.3536 36.1163 14.3274 36.1375 14.2975C36.1674 14.2576 36.1923 14.2165 36.2272 14.1816C36.2559 14.1529 36.292 14.1317 36.3244 14.1068C36.3618 14.0769 36.3942 14.0445 36.4341 14.0208L48.4147 7.12434C48.5663 7.03694 48.7383 6.99094 48.9133 6.99094C49.0883 6.99094 49.2602 7.03694 49.4118 7.12434L61.3899 14.0208C61.4323 14.0457 61.4647 14.0769 61.5021 14.1055C61.5333 14.1305 61.5694 14.1529 61.5981 14.1803C61.633 14.2165 61.6579 14.2576 61.6878 14.2975C61.7103 14.3274 61.7377 14.3536 61.7551 14.386C61.7838 14.4346 61.8 14.4882 61.8199 14.5405C61.8312 14.5692 61.8474 14.5954 61.8548 14.6253ZM59.893 27.9844V16.6121L55.7013 19.0252L49.9104 22.3593V33.7317L59.8942 27.9844H59.893ZM47.9149 48.5566V37.1768L42.2187 40.4299L25.953 49.7133V61.2003L47.9149 48.5566ZM1.99677 9.83281V48.5566L23.9562 61.199V49.7145L12.4841 43.2219L12.4804 43.2194L12.4754 43.2169C12.4368 43.1945 12.4044 43.1621 12.3682 43.1347C12.3371 43.1097 12.3009 43.0898 12.2735 43.0624L12.271 43.0586C12.2386 43.0275 12.2162 42.9888 12.1887 42.9539C12.1638 42.9203 12.1339 42.8916 12.114 42.8567L12.1127 42.853C12.0903 42.8156 12.0766 42.7707 12.0604 42.7283C12.0442 42.6909 12.023 42.656 12.013 42.6161C12.0005 42.5688 11.998 42.5177 11.9931 42.4691C11.9881 42.4317 11.9781 42.3943 11.9781 42.3569V15.5801L6.18848 12.2446L1.99677 9.83281ZM12.9777 2.36177L2.99764 8.10652L12.9752 13.8513L22.9541 8.10527L12.9752 2.36177H12.9777ZM18.1678 38.2138L23.9574 34.8809V9.83281L19.7657 12.2459L13.9749 15.5801V40.6281L18.1678 38.2138ZM48.9133 9.14105L38.9344 14.8858L48.9133 20.6305L58.8909 14.8846L48.9133 9.14105ZM47.9149 22.3593L42.124 19.0252L37.9323 16.6121V27.9844L43.7219 31.3174L47.9149 33.7317V22.3593ZM24.9533 47.987L39.59 39.631L46.9065 35.4555L36.9352 29.7145L25.4544 36.3242L14.9907 42.3482L24.9533 47.987Z" />
    </svg>
    <p class="mt-2 font-medium">Laravel</p>
  </LinkedCard>
  <LinkedCard href="/docs/installation/manual">
    <svg class="w-10 h-10" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M19.114 2H15l-3 4.9L9.429 2H0l12 21L24 2zM3 3.75h2.914L12 14.6l6.086-10.85H21L12 19.5z"/></svg>
    <p class="mt-2 font-medium">Manual</p>
  </LinkedCard>
</div>

## Upgrade Your Project

<Callout class="bg-blue-50 mt-6 border-blue-600 dark:border-blue-900 dark:bg-blue-950 mb-6 [&_code]:bg-blue-100 dark:[&_code]:bg-blue-900 [&_a]:underline [&_a]:underline-offset-4">

  **Important:** Before upgrading, please read the [Tailwind v4 Compatibility
  Docs](https://tailwindcss.com/docs/compatibility) and make sure your project
  is ready for the upgrade. Tailwind v4 uses bleeding-edge browser features and
  is designed for modern browsers.

</Callout>

One of the major advantages of using `shadcn-vue` is that the code you end up with is exactly what you'd write yourself. There are no hidden abstractions.

This means when a dependency has a new release, you can just follow the official upgrade paths.

Here's how to upgrade your existing projects (full docs are on the way):

### 1. Follow the Tailwind v4 Upgrade Guide

- Upgrade to Tailwind v4 by following the official upgrade guide: https://tailwindcss.com/docs/upgrade-guide
- Use the `@tailwindcss/upgrade@next` codemod to remove deprecated utility classes and update tailwind config.

### 2. Update your CSS variables

The codemod will migrate your CSS variables as references under the `@theme` directive.

```css showLineNumbers
@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 0 0% 3.9%;
  }
}

@theme {
  --color-background: hsl(var(--background));
  --color-foreground: hsl(var(--foreground));
}
```

This works. But to make it easier to work with colors and other variables, we'll need to move the `hsl` wrappers and use `@theme inline`.

Here's how you do it:

1. Move `:root` and `.dark` out of the `@layer` base.
2. Wrap the color values in `hsl()`
3. Add the `inline` option to `@theme` i.e `@theme inline`
4. Remove the `hsl()` wrappers from `@theme`

```css showLineNumbers
:root {
  --background: hsl(0 0% 100%); // <-- Wrap in hsl
  --foreground: hsl(0 0% 3.9%);
}

.dark {
  --background: hsl(0 0% 3.9%); // <-- Wrap in hsl
  --foreground: hsl(0 0% 98%);
}

@theme inline {
  --color-background: var(--background); // <-- Remove hsl
  --color-foreground: var(--foreground);
}
```

This change makes it much simpler to access your theme variables in both utility classes and outside of CSS for eg. using color values in JavaScript.

### 3. Use new `size-*` utility

The new `size-*` utility (added in Tailwind v3.4), is now fully supported by `tailwind-merge`. You can replace `w-* h-*` with the new `size-*` utility:

```diff
- w-4 h-4
+ size-4
```

### 4. Install and Update your dependencies

```bash
pnpm i tw-animate-css
pnpm up reka-ui @lucide/vue tailwind-merge clsx --latest
```

---

### Apps V4 Content Docs .Typography (`apps/v4/content/docs/.typography.md`)

---
title: Typography
description: Styles for headings, paragraphs, lists...etc
component: true
---

<ComponentPreview name="TypographyDemo" />

## h1

<ComponentPreview name="TypographyH1" />

## h2

<ComponentPreview name="TypographyH2" />

## h3

<ComponentPreview name="TypographyH3" />

## h4

<ComponentPreview name="TypographyH4" />

## p

<ComponentPreview name="TypographyP" />

## blockquote

<ComponentPreview name="TypographyBlockquote" />

## table

<ComponentPreview name="TypographyTable" />

## list

<ComponentPreview name="TypographyList" />

## Inline code

<ComponentPreview name="TypographyInlineCode" />

## Lead

<ComponentPreview name="TypographyLead" />

## Large

<ComponentPreview name="TypographyLarge" />

## Small

<ComponentPreview name="TypographySmall" />

## Muted

<ComponentPreview name="TypographyMuted" />

---

### Apps V4 Content Docs 01.Introduction (`apps/v4/content/docs/01.introduction.md`)

---
title: Introduction
description: shadcn-vue is a set of beautifully-designed, accessible components and a code distribution platform. Works with your favorite frameworks and AI models. Open Source. Open Code.
---

**This is not a component library. It is how you build your component library.**

::vue-school-link{lesson="what-is-shadcn-vue" placement="top"}
Watch a Vue School intro video about shadcn-vue.
::

You know how most traditional component libraries work: you install a package from NPM, import the components, and use them in your app.

This approach works well until you need to customize a component to fit your design system or require one that isn't included in the library. **Often, you end up wrapping library components, writing workarounds to override styles, or mixing components from different libraries with incompatible APIs.**

This is what shadcn-vue aims to solve. It is built around the following principles:

- **Open Code:** The top layer of your component code is open for modification.
- **Composition:** Every component uses a common, composable interface, making them predictable.
- **Distribution:** A flat-file schema and command-line tool make it easy to distribute components.
- **Beautiful Defaults:** Carefully chosen default styles, so you get great design out-of-the-box.
- **AI-Ready:** Open code for LLMs to read, understand, and improve.

## Open Code

shadcn-vue hands you the actual component code. You have full control to customize and extend the components to your needs. This means:

- **Full Transparency:** You see exactly how each component is built.
- **Easy Customization:** Modify any part of a component to fit your design and functionality requirements.
- **AI Integration:** Access to the code makes it straightforward for LLMs to read, understand, and even improve your components.

_In a typical library, if you need to change a button's behavior, you have to override styles or wrap the component. With shadcn-vue, you simply edit the button code directly._

<accordion collapsible>
  <accordion-item value="faq-1" class="border-none">
    <accordion-trigger>
      How do I pull upstream updates in an Open Code approach?
    </accordion-trigger>
    <accordion-content>
      <p>
        shadcn-vue follows a headless component architecture. This means the core
        of your app can receive fixes by updating your dependencies, for
        instance, radix-ui or input-otp.
      </p>
      <p class="mt-4">
        The topmost layer, i.e., the one closest to your design system, is not
        coupled with the implementation of the library. It stays open for
        modification.
      </p>
    </accordion-content>
  </accordion-item>
</accordion>

## Composition

Every component in shadcn-vue shares a common, composable interface. **If a component does not exist, we bring it in, make it composable, and adjust its style to match and work with the rest of the design system.**

_A shared, composable interface means it's predictable for both your team and LLMs. You are not learning different APIs for every new component. Even for third-party ones._

## Distribution

shadcn-vue is also a code distribution system. It defines a schema for components and a CLI to distribute them.

- **Schema:** A flat-file structure that defines the components, their dependencies, and properties.
- **CLI:** A command-line tool to distribute and install components across projects with cross-framework support.

_You can use the schema to distribute your components to other projects or have AI generate completely new components based on existing schema._

## Beautiful Defaults

shadcn-vue comes with a large collection of components that have carefully chosen default styles. They are designed to look good on their own and to work well together as a consistent system:

- **Good Out-of-the-Box:** Your UI has a clean and minimal look without extra work.
- **Unified Design:** Components naturally fit with one another. Each component is built to match the others, keeping your UI consistent.
- **Easily Customizable:** If you want to change something, it's simple to override and extend the defaults.

## AI-Ready

The design of shadcn-vue makes it easy for AI tools to work with your code. Its open code and consistent API allow AI models to read, understand, and even generate new components.

_An AI model can learn how your components work and suggest improvements or even create new components that integrate with your existing design._

---

### Apps V4 Content Docs 02.Components (`apps/v4/content/docs/02.components.md`)

---
title: Components
description: Here you can find all the components available in the library. We are working on adding more components.
---

<ComponentsList />

---

### Apps V4 Content Docs 02.Installation (`apps/v4/content/docs/02.installation.md`)

---
title: Installation
description: How to install dependencies and structure your app.
---

## Pick Your Framework

Start by selecting your framework of choice. Then follow the instructions to install the dependencies and structure your app. shadcn-vue is built to work with all Vue frameworks.

<div class="mt-8 grid gap-4 sm:grid-cols-2 sm:gap-6">
  <linked-card href="/docs/installation/vite">
    <svg
      role="img"
      viewBox="0 0 24 24"
      xmlns="http://www.w3.org/2000/svg"
      class="w-10 h-10"
      fill="currentColor"
    >
      <title>Vite</title>
      <path d="m8.286 10.578.512-8.657a.306.306 0 0 1 .247-.282L17.377.006a.306.306 0 0 1 .353.385l-1.558 5.403a.306.306 0 0 0 .352.385l2.388-.46a.306.306 0 0 1 .332.438l-6.79 13.55-.123.19a.294.294 0 0 1-.252.14c-.177 0-.35-.152-.305-.369l1.095-5.301a.306.306 0 0 0-.388-.355l-1.433.435a.306.306 0 0 1-.389-.354l.69-3.375a.306.306 0 0 0-.37-.36l-2.32.536a.306.306 0 0 1-.374-.316zm14.976-7.926L17.284 3.74l-.544 1.887 2.077-.4a.8.8 0 0 1 .84.369.8.8 0 0 1 .034.783L12.9 19.93l-.013.025-.015.023-.122.19a.801.801 0 0 1-.672.37.826.826 0 0 1-.634-.302.8.8 0 0 1-.16-.67l1.029-4.981-1.12.34a.81.81 0 0 1-.86-.262.802.802 0 0 1-.165-.67l.63-3.08-2.027.468a.808.808 0 0 1-.768-.233.81.81 0 0 1-.217-.6l.389-6.57-7.44-1.33a.612.612 0 0 0-.64.906L11.58 23.691a.612.612 0 0 0 1.066-.004l11.26-20.135a.612.612 0 0 0-.644-.9z" />
    </svg>
    <p class="mt-2 font-medium  [&:not(:first-child)]:mt-2">Vite</p>
  </linked-card>
  <linked-card href="/docs/installation/nuxt">
    <svg xmlns="http://www.w3.org/2000/svg"  class="w-12 h-12" viewBox="0 0 900 900" fill="none">
    <title>Nuxt</title>
    <path d="M504.908 750H839.476C850.103 750.001 860.542 747.229 869.745 741.963C878.948 736.696 886.589 729.121 891.9 719.999C897.211 710.876 900.005 700.529 900 689.997C899.995 679.465 897.193 669.12 891.873 660.002L667.187 274.289C661.876 265.169 654.237 257.595 645.036 252.329C635.835 247.064 625.398 244.291 614.773 244.291C604.149 244.291 593.711 247.064 584.511 252.329C575.31 257.595 567.67 265.169 562.36 274.289L504.908 372.979L392.581 179.993C387.266 170.874 379.623 163.301 370.42 158.036C361.216 152.772 350.777 150 340.151 150C329.525 150 319.086 152.772 309.883 158.036C300.679 163.301 293.036 170.874 287.721 179.993L8.12649 660.002C2.80743 669.12 0.00462935 679.465 5.72978e-06 689.997C-0.00461789 700.529 2.78909 710.876 8.10015 719.999C13.4112 729.121 21.0523 736.696 30.255 741.963C39.4576 747.229 49.8973 750.001 60.524 750H270.538C353.748 750 415.112 713.775 457.336 643.101L559.849 467.145L614.757 372.979L779.547 655.834H559.849L504.908 750ZM267.114 655.737L120.551 655.704L340.249 278.586L449.87 467.145L376.474 593.175C348.433 639.03 316.577 655.737 267.114 655.737Z" fill="currentColor"/>
    </svg>
    <p class="mt-2 font-medium  [&:not(:first-child)]:mt-2">Nuxt</p>
  </linked-card>
  <linked-card href="/docs/installation/astro">
    <svg
      role="img"
      viewBox="0 0 24 24"
      xmlns="http://www.w3.org/2000/svg"
      class="w-10 h-10"
      fill="currentColor"
    >
      <title>Astro</title>
      <path
        d="M16.074 16.86C15.354 17.476 13.917 17.895 12.262 17.895C10.23 17.895 8.527 17.263 8.075 16.412C7.914 16.9 7.877 17.458 7.877 17.814C7.877 17.814 7.771 19.564 8.988 20.782C8.988 20.15 9.501 19.637 10.133 19.637C11.216 19.637 11.215 20.582 11.214 21.349V21.418C11.214 22.582 11.925 23.579 12.937 24C12.7812 23.6794 12.7005 23.3275 12.701 22.971C12.701 21.861 13.353 21.448 14.111 20.968C14.713 20.585 15.383 20.161 15.844 19.308C16.0926 18.8493 16.2225 18.3357 16.222 17.814C16.2221 17.4903 16.1722 17.1685 16.074 16.86ZM15.551 0.6C15.747 0.844 15.847 1.172 16.047 1.829L20.415 16.176C18.7743 15.3246 17.0134 14.7284 15.193 14.408L12.35 4.8C12.3273 4.72337 12.2803 4.65616 12.2162 4.60844C12.152 4.56072 12.0742 4.53505 11.9943 4.53528C11.9143 4.5355 11.8366 4.56161 11.7727 4.60969C11.7089 4.65777 11.6623 4.72524 11.64 4.802L8.83 14.405C7.00149 14.724 5.23264 15.3213 3.585 16.176L7.974 1.827C8.174 1.171 8.274 0.843 8.471 0.6C8.64406 0.385433 8.86922 0.218799 9.125 0.116C9.415 0 9.757 0 10.443 0H13.578C14.264 0 14.608 0 14.898 0.117C15.1529 0.219851 15.3783 0.386105 15.551 0.6Z"
        fill="currentColor"
      />
    </svg>
    <p class="mt-2 font-medium  [&:not(:first-child)]:mt-2">Astro</p>
  </linked-card>
  <linked-card href="/docs/installation/laravel">
    <svg
      role="img"
      viewBox="0 0 62 65"
      fill="currentColor"
      xmlns="http://www.w3.org/2000/svg"
      class="w-10 h-10"
    >
      <path d="M61.8548 14.6253C61.8778 14.7102 61.8895 14.7978 61.8897 14.8858V28.5615C61.8898 28.737 61.8434 28.9095 61.7554 29.0614C61.6675 29.2132 61.5409 29.3392 61.3887 29.4265L49.9104 36.0351V49.1337C49.9104 49.4902 49.7209 49.8192 49.4118 49.9987L25.4519 63.7916C25.3971 63.8227 25.3372 63.8427 25.2774 63.8639C25.255 63.8714 25.2338 63.8851 25.2101 63.8913C25.0426 63.9354 24.8666 63.9354 24.6991 63.8913C24.6716 63.8838 24.6467 63.8689 24.6205 63.8589C24.5657 63.8389 24.5084 63.8215 24.456 63.7916L0.501061 49.9987C0.348882 49.9113 0.222437 49.7853 0.134469 49.6334C0.0465019 49.4816 0.000120578 49.3092 0 49.1337L0 8.10652C0 8.01678 0.0124642 7.92953 0.0348998 7.84477C0.0423783 7.8161 0.0598282 7.78993 0.0697995 7.76126C0.0884958 7.70891 0.105946 7.65531 0.133367 7.6067C0.152063 7.5743 0.179485 7.54812 0.20192 7.51821C0.230588 7.47832 0.256763 7.43719 0.290416 7.40229C0.319084 7.37362 0.356476 7.35243 0.388883 7.32751C0.425029 7.29759 0.457436 7.26518 0.498568 7.2415L12.4779 0.345059C12.6296 0.257786 12.8015 0.211853 12.9765 0.211853C13.1515 0.211853 13.3234 0.257786 13.475 0.345059L25.4531 7.2415H25.4556C25.4955 7.26643 25.5292 7.29759 25.5653 7.32626C25.5977 7.35119 25.6339 7.37362 25.6625 7.40104C25.6974 7.43719 25.7224 7.47832 25.7523 7.51821C25.7735 7.54812 25.8021 7.5743 25.8196 7.6067C25.8483 7.65656 25.8645 7.70891 25.8844 7.76126C25.8944 7.78993 25.9118 7.8161 25.9193 7.84602C25.9423 7.93096 25.954 8.01853 25.9542 8.10652V33.7317L35.9355 27.9844V14.8846C35.9355 14.7973 35.948 14.7088 35.9704 14.6253C35.9792 14.5954 35.9954 14.5692 36.0053 14.5405C36.0253 14.4882 36.0427 14.4346 36.0702 14.386C36.0888 14.3536 36.1163 14.3274 36.1375 14.2975C36.1674 14.2576 36.1923 14.2165 36.2272 14.1816C36.2559 14.1529 36.292 14.1317 36.3244 14.1068C36.3618 14.0769 36.3942 14.0445 36.4341 14.0208L48.4147 7.12434C48.5663 7.03694 48.7383 6.99094 48.9133 6.99094C49.0883 6.99094 49.2602 7.03694 49.4118 7.12434L61.3899 14.0208C61.4323 14.0457 61.4647 14.0769 61.5021 14.1055C61.5333 14.1305 61.5694 14.1529 61.5981 14.1803C61.633 14.2165 61.6579 14.2576 61.6878 14.2975C61.7103 14.3274 61.7377 14.3536 61.7551 14.386C61.7838 14.4346 61.8 14.4882 61.8199 14.5405C61.8312 14.5692 61.8474 14.5954 61.8548 14.6253ZM59.893 27.9844V16.6121L55.7013 19.0252L49.9104 22.3593V33.7317L59.8942 27.9844H59.893ZM47.9149 48.5566V37.1768L42.2187 40.4299L25.953 49.7133V61.2003L47.9149 48.5566ZM1.99677 9.83281V48.5566L23.9562 61.199V49.7145L12.4841 43.2219L12.4804 43.2194L12.4754 43.2169C12.4368 43.1945 12.4044 43.1621 12.3682 43.1347C12.3371 43.1097 12.3009 43.0898 12.2735 43.0624L12.271 43.0586C12.2386 43.0275 12.2162 42.9888 12.1887 42.9539C12.1638 42.9203 12.1339 42.8916 12.114 42.8567L12.1127 42.853C12.0903 42.8156 12.0766 42.7707 12.0604 42.7283C12.0442 42.6909 12.023 42.656 12.013 42.6161C12.0005 42.5688 11.998 42.5177 11.9931 42.4691C11.9881 42.4317 11.9781 42.3943 11.9781 42.3569V15.5801L6.18848 12.2446L1.99677 9.83281ZM12.9777 2.36177L2.99764 8.10652L12.9752 13.8513L22.9541 8.10527L12.9752 2.36177H12.9777ZM18.1678 38.2138L23.9574 34.8809V9.83281L19.7657 12.2459L13.9749 15.5801V40.6281L18.1678 38.2138ZM48.9133 9.14105L38.9344 14.8858L48.9133 20.6305L58.8909 14.8846L48.9133 9.14105ZM47.9149 22.3593L42.124 19.0252L37.9323 16.6121V27.9844L43.7219 31.3174L47.9149 33.7317V22.3593ZM24.9533 47.987L39.59 39.631L46.9065 35.4555L36.9352 29.7145L25.4544 36.3242L14.9907 42.3482L24.9533 47.987Z" />
    </svg>
    <p class="mt-2 font-medium  [&:not(:first-child)]:mt-2">Laravel</p>
  </linked-card>
  <linked-card href="/docs/installation/manual">
    <svg class="w-10 h-10" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M19.114 2H15l-3 4.9L9.429 2H0l12 21L24 2zM3 3.75h2.914L12 14.6l6.086-10.85H21L12 19.5z"/></svg>
    <p class="mt-2 font-medium  [&:not(:first-child)]:mt-2">Manual</p>
  </linked-card>
</div>

## VSCode extension

Install the [shadcn-vue](https://marketplace.visualstudio.com/items?itemName=Selemondev.shadcn-vue) extension by [@selemondev](https://github.com/selemondev) in Visual Studio Code to easily add Shadcn Vue components to your project.

This extension offers a range of features:
- Ability to initialize the Shadcn Vue CLI
- Install components
- Open documentation
- Navigate to a specific component's documentation page directly from your IDE.
- Handy snippets for quick and straightforward component imports and markup.

---

### Apps V4 Content Docs 03.Components Json (`apps/v4/content/docs/03.components-json.md`)

---
title: components.json
description: Configuration for your project.
---

The `components.json` file holds configuration for your project.

We use it to understand how your project is set up and how to generate components customized for your project.

<Callout class="mt-6" title="Note: The `components.json` file is optional">

  It is **only required if you're using the CLI** to add components to your
  project. If you're using the copy and paste method, you don't need this file.

</Callout>

You can create a `components.json` file in your project by running the following command:

```bash
npx shadcn-vue@latest init
```

See the [CLI section](/docs/cli) for more information.

## $schema

You can see the JSON Schema for `components.json` [here](https://shadcn-vue.com/schema.json).

```json title="components.json"
{
  "$schema": "https://shadcn-vue.com/schema.json"
}
```

## style

The style for your components. **This cannot be changed after initialization.**

```json title="components.json"
{
  "style": "new-york"
}
```

The `default` style has been deprecated. Use the `new-york` style instead.

## tailwind

Configuration to help the CLI understand how Tailwind CSS is set up in your project.

See the <a href="/docs/installation">installation section</a> for how to set up Tailwind CSS.

### tailwind.config

Path to where your `tailwind.config.js` file is located. **For Tailwind CSS v4, leave this blank.**

```json title="components.json"
{
  "tailwind": {
    "config": "tailwind.config.js" | "tailwind.config.ts"
  }
}
```

### tailwind.css

Path to the CSS file that imports Tailwind CSS into your project.

```json title="components.json"
{
  "tailwind": {
    "css": "styles/global.css"
  }
}
```

### tailwind.baseColor

This is used to generate the default color palette for your components. **This cannot be changed after initialization.**

```json title="components.json"
{
  "tailwind": {
    "baseColor": "gray" | "neutral" | "slate" | "stone" | "zinc"
  }
}
```

### tailwind.cssVariables

You can choose between using CSS variables or Tailwind CSS utility classes for theming.

To use utility classes for theming set `tailwind.cssVariables` to `false`. For CSS variables, set `tailwind.cssVariables` to `true`.

```json title="components.json"
{
  "tailwind": {
    "cssVariables": `true` | `false`
  }
}
```

For more information, see the <Link href="/docs/theming">theming docs</Link>.

**This cannot be changed after initialization.** To switch between CSS variables and utility classes, you'll have to delete and re-install your components.

### tailwind.prefix

The prefix to use for your Tailwind CSS utility classes. Components will be added with this prefix.

```json title="components.json"
{
  "tailwind": {
    "prefix": "tw-"
  }
}
```

## typescript

Choose between TypeScript or JavaScript components.

Setting this option to `false` allows components to be added as JavaScript in `.vue` file.

```json title="components.json"
{
  "typescript": `true` | `false`
}
```

## aliases

The CLI uses these values and the `paths` config from your `tsconfig.json` or `jsconfig.json` file to place generated components in the correct location.

Path aliases have to be set up in your `tsconfig.json` or `jsconfig.json` file.

<Callout class="mt-6">

  **Important:** If you're using the `src` directory, make sure it is included
  under `paths` in your `tsconfig.json` or `jsconfig.json` file.

</Callout>

### aliases.utils

Import alias for your utility functions.

```json title="components.json"
{
  "aliases": {
    "utils": "@/lib/utils"
  }
}
```

### aliases.components

Import alias for your components.

```json title="components.json"
{
  "aliases": {
    "components": "@/components"
  }
}
```

### aliases.ui

Import alias for `ui` components.

The CLI will use the `aliases.ui` value to determine where to place your `ui` components. Use this config if you want to customize the installation directory for your `ui` components.

```json title="components.json"
{
  "aliases": {
    "ui": "@/app/ui"
  }
}
```

### aliases.lib

Import alias for `lib` functions such as `cn`.

```json title="components.json"
{
  "aliases": {
    "lib": "@/lib"
  }
}
```

### aliases.composables

Import alias for `composables` such as `useMediaQuery` or `useToast`.

```json title="components.json"
{
  "aliases": {
    "composables": "@/composables"
  }
}
```

---

### Apps V4 Content Docs 04.Theming (`apps/v4/content/docs/04.theming.md`)

---
title: Theming
description: Using CSS variables and theme tokens.
---

::vue-school-link{lesson="theming-with-shadcn-vue" placement="top"}
Watch a Vue School video about theming with shadcn-vue.
::

<Callout>

Want to build your theme visually? Use [shadcn-vue/create](/create) to preview colors, radius, fonts, and icons, then generate a preset for your project.

</Callout>

We use and recommend CSS variables for theming.

This gives you semantic theme tokens like `background`, `foreground`, and `primary` that components use by default. Override those tokens in your CSS to change the look of your app without rewriting component classes.

```vue /bg-background/ /text-foreground/
<div class="bg-background text-foreground" />
```

To use CSS variables for theming, set `tailwind.cssVariables` to `true` in your `components.json` file. This is the default.

```json {8} title="components.json" showLineNumbers
{
  "style": "default",
  "typescript": true,
  "tailwind": {
    "config": "",
    "css": "assets/css/tailwind.css",
    "baseColor": "neutral",
    "cssVariables": true
  }
}
```

Tailwind maps these tokens into utilities like `bg-background`, `text-foreground`, `border-border`, and `ring-ring`.

Dark mode works by overriding the same tokens inside a `.dark` selector. See the [dark mode docs](/docs/dark-mode) for adding a theme provider and toggling the `.dark` class.

## Token Convention

We use semantic background and foreground pairs. The base token controls the surface color and the `-foreground` token controls the text and icon color that sits on that surface.

<Callout class="mt-4">

The background suffix is omitted for the surface token. For example, `primary` pairs with `primary-foreground`.

</Callout>

Given the following CSS variables:

```css
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
```

The `background` color of the following component will be `var(--primary)` and the `foreground` color will be `var(--primary-foreground)`.

```vue
<div class="bg-primary text-primary-foreground">Hello</div>
```

## Theme Tokens

These tokens live in your CSS file under `:root` and `.dark`.

| Token                                            | What it controls                                       | Used by                                                                      |
| ------------------------------------------------ | ------------------------------------------------------ | ---------------------------------------------------------------------------- |
| `background` / `foreground`                      | The default app background and text color.             | The page shell, page sections, and default text.                             |
| `card` / `card-foreground`                       | Elevated surfaces and the content inside them.         | `Card`, dashboard panels, settings panels.                                   |
| `popover` / `popover-foreground`                 | Floating surfaces and the content inside them.         | `Popover`, `DropdownMenu`, `ContextMenu`, and other overlays.                |
| `primary` / `primary-foreground`                 | High-emphasis actions and brand surfaces.              | Default `Button`, selected states, badges, and active accents.               |
| `secondary` / `secondary-foreground`             | Lower-emphasis filled actions and supporting surfaces. | Secondary buttons, secondary badges, and supporting UI.                      |
| `muted` / `muted-foreground`                     | Subtle surfaces and lower-emphasis content.            | Descriptions, placeholders, empty states, helper text, and subdued surfaces. |
| `accent` / `accent-foreground`                   | Interactive hover, focus, and active surfaces.         | Ghost buttons, menu highlight states, hovered rows, and selected items.      |
| `destructive`                                    | Destructive actions and error emphasis.                | Destructive buttons, invalid states, and destructive menu items.             |
| `border`                                         | Default borders and separators.                        | Cards, menus, tables, separators, and layout dividers.                       |
| `input`                                          | Form control borders and input surface treatment.      | `Input`, `Textarea`, `Select`, and outline-style controls.                   |
| `ring`                                           | Focus rings and outlines.                              | Buttons, inputs, checkboxes, menus, and other focusable controls.            |
| `chart-1` ... `chart-5`                          | The default chart palette.                             | Charts and chart-driven dashboard blocks.                                    |
| `sidebar` / `sidebar-foreground`                 | The base sidebar surface and default sidebar text.     | The `Sidebar` container and its default content.                             |
| `sidebar-primary` / `sidebar-primary-foreground` | High-emphasis actions inside the sidebar.              | Active items, icon tiles, badges, and sidebar CTAs.                          |
| `sidebar-accent` / `sidebar-accent-foreground`   | Hover and selected states inside the sidebar.          | Sidebar menu hover states, open items, and interactive rows.                 |
| `sidebar-border`                                 | Sidebar-specific borders and separators.               | Sidebar headers, groups, and internal dividers.                              |
| `sidebar-ring`                                   | Sidebar-specific focus rings.                          | Focused controls inside the sidebar.                                         |
| `radius`                                         | The base corner radius scale.                          | Cards, inputs, buttons, popovers, and the derived `radius-*` tokens.         |

<Callout class="mt-4">

The chart tokens are covered in more detail in the [Chart theming docs](/docs/components/chart#theming).

</Callout>

## Radius Scale

`--radius` is the base radius token for your theme.

We derive a small radius scale from it so components can use consistent corner sizes while still sharing a single source of truth.

```css title="assets/css/tailwind.css" showLineNumbers
@theme inline {
  --radius-sm: calc(var(--radius) * 0.6);
  --radius-md: calc(var(--radius) * 0.8);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) * 1.4);
  --radius-2xl: calc(var(--radius) * 1.8);
  --radius-3xl: calc(var(--radius) * 2.2);
  --radius-4xl: calc(var(--radius) * 2.6);
}
```

This means:

- `radius-lg` is the base value.
- Smaller radii scale down from `--radius`.
- Larger radii scale up from `--radius`.
- Changing `--radius` updates the entire radius scale.

## Adding New Tokens

To add a new token, define it under `:root` and `.dark`, then expose it to Tailwind with `@theme inline`.

```css title="assets/css/tailwind.css" showLineNumbers
:root {
  --warning: oklch(0.84 0.16 84);
  --warning-foreground: oklch(0.28 0.07 46);
}

.dark {
  --warning: oklch(0.41 0.11 46);
  --warning-foreground: oklch(0.99 0.02 95);
}

@theme inline {
  --color-warning: var(--warning);
  --color-warning-foreground: var(--warning-foreground);
}
```

You can now use `bg-warning` and `text-warning-foreground` in your components.

```vue /bg-warning/ /text-warning-foreground/
<div class="bg-warning text-warning-foreground" />
```

## Base Colors

`tailwind.baseColor` controls the default token values generated for your project when you run `init` or use a preset.

The available base colors are: **Neutral**, **Gray**, **Zinc**, **Stone**, and **Slate**.

## Default Theme CSS

The following is the full default `neutral` theme scaffold. Copy it into your global CSS file and adjust the tokens as needed.

::code-collapsible-wrapper

```
/* Detailed source-code truncated for AI context efficiency. */
```

::

## Without CSS Variables

If you do not want to use CSS variables, the CLI can generate components with inline Tailwind color utilities instead.

```bash
npx shadcn-vue@latest init --no-css-variables
```

This sets `tailwind.cssVariables` to `false` in your `components.json` file.

```vue /bg-zinc-950/ /text-zinc-50/ /dark:bg-white/ /dark:text-zinc-950/
<div class="bg-zinc-950 text-zinc-50 dark:bg-white dark:text-zinc-950" />
```

<Callout class="mt-4">

This is an installation-time choice. To switch an existing project, delete and re-install your components.

</Callout>

---

### Apps V4 Content Docs 05.Dark Mode (`apps/v4/content/docs/05.dark-mode.md`)

---
title: Dark Mode
description: Adding dark mode to your site.
---

<div class="grid sm:grid-cols-2 gap-4 sm:gap-6">
  <linked-card href="/docs/dark-mode/vite">
    <svg
      role="img"
      viewBox="0 0 24 24"
      xmlns="http://www.w3.org/2000/svg"
      class="w-10 h-10"
      fill="currentColor"
    >
      <title>Vite</title>
      <path d="m8.286 10.578.512-8.657a.306.306 0 0 1 .247-.282L17.377.006a.306.306 0 0 1 .353.385l-1.558 5.403a.306.306 0 0 0 .352.385l2.388-.46a.306.306 0 0 1 .332.438l-6.79 13.55-.123.19a.294.294 0 0 1-.252.14c-.177 0-.35-.152-.305-.369l1.095-5.301a.306.306 0 0 0-.388-.355l-1.433.435a.306.306 0 0 1-.389-.354l.69-3.375a.306.306 0 0 0-.37-.36l-2.32.536a.306.306 0 0 1-.374-.316zm14.976-7.926L17.284 3.74l-.544 1.887 2.077-.4a.8.8 0 0 1 .84.369.8.8 0 0 1 .034.783L12.9 19.93l-.013.025-.015.023-.122.19a.801.801 0 0 1-.672.37.826.826 0 0 1-.634-.302.8.8 0 0 1-.16-.67l1.029-4.981-1.12.34a.81.81 0 0 1-.86-.262.802.802 0 0 1-.165-.67l.63-3.08-2.027.468a.808.808 0 0 1-.768-.233.81.81 0 0 1-.217-.6l.389-6.57-7.44-1.33a.612.612 0 0 0-.64.906L11.58 23.691a.612.612 0 0 0 1.066-.004l11.26-20.135a.612.612 0 0 0-.644-.9z" />
    </svg>
    <p class="mt-2 [&:not(:first-child)]:mt-2 font-medium">Vite</p>
  </linked-card>

  <linked-card href="/docs/dark-mode/nuxt">
    <svg viewBox="0 0 24 24" width="1.2em" height="1.2em" class="w-10 h-10"><path fill="currentColor" d="M13.464 19.83h8.922c.283 0 .562-.073.807-.21a1.6 1.6 0 0 0 .591-.574a1.53 1.53 0 0 0 .216-.783a1.53 1.53 0 0 0-.217-.782L17.792 7.414a1.6 1.6 0 0 0-.591-.573a1.65 1.65 0 0 0-.807-.21c-.283 0-.562.073-.807.21a1.6 1.6 0 0 0-.59.573L13.463 9.99L10.47 4.953a1.6 1.6 0 0 0-.591-.573a1.65 1.65 0 0 0-.807-.21c-.284 0-.562.073-.807.21a1.6 1.6 0 0 0-.591.573L.216 17.481a1.53 1.53 0 0 0-.217.782c0 .275.074.545.216.783a1.6 1.6 0 0 0 .59.574c.246.137.525.21.808.21h5.6c2.22 0 3.856-.946 4.982-2.79l2.733-4.593l1.464-2.457l4.395 7.382h-5.859Zm-6.341-2.46l-3.908-.002l5.858-9.842l2.923 4.921l-1.957 3.29c-.748 1.196-1.597 1.632-2.916 1.632"></path></svg>
    <p class="mt-2 [&:not(:first-child)]:mt-2 font-medium">Nuxt</p>
  </linked-card>

  <linked-card href="/docs/dark-mode/vitepress">
    <svg width="48" height="48" viewBox="0 0 48 48" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
      <path d="M5.03628 7.87818C4.75336 5.83955 6.15592 3.95466 8.16899 3.66815L33.6838 0.0367403C35.6969 -0.24977 37.5581 1.1706 37.841 3.20923L42.9637 40.1218C43.2466 42.1604 41.8441 44.0453 39.831 44.3319L14.3162 47.9633C12.3031 48.2498 10.4419 46.8294 10.159 44.7908L5.03628 7.87818Z" />
      <path d="M6.85877 7.6188C6.71731 6.59948 7.41859 5.65703 8.42512 5.51378L33.9399 1.88237C34.9465 1.73911 35.8771 2.4493 36.0186 3.46861L41.1412 40.3812C41.2827 41.4005 40.5814 42.343 39.5749 42.4862L14.0601 46.1176C13.0535 46.2609 12.1229 45.5507 11.9814 44.5314L6.85877 7.6188Z" class="fill-background"/>
      <path d="M33.1857 14.9195L25.8505 34.1576C25.6991 34.5547 25.1763 34.63 24.9177 34.2919L12.3343 17.8339C12.0526 17.4655 12.3217 16.9339 12.7806 16.9524L22.9053 17.3607C22.9698 17.3633 23.0344 17.3541 23.0956 17.3337L32.5088 14.1992C32.9431 14.0546 33.3503 14.4878 33.1857 14.9195Z" />
      <path d="M27.0251 12.5756L19.9352 15.0427C19.8187 15.0832 19.7444 15.1986 19.7546 15.3231L20.3916 23.063C20.4066 23.2453 20.5904 23.3628 20.7588 23.2977L22.7226 22.5392C22.9064 22.4682 23.1021 22.6138 23.0905 22.8128L22.9102 25.8903C22.8982 26.0974 23.1093 26.2436 23.295 26.1567L24.4948 25.5953C24.6808 25.5084 24.892 25.6549 24.8795 25.8624L24.5855 30.6979C24.5671 31.0004 24.9759 31.1067 25.1013 30.8321L25.185 30.6487L29.4298 17.8014C29.5008 17.5863 29.2968 17.3809 29.0847 17.454L27.0519 18.1547C26.8609 18.2205 26.6675 18.0586 26.6954 17.8561L27.3823 12.8739C27.4103 12.6712 27.2163 12.5091 27.0251 12.5756Z" class="stroke-background"/>
    </svg>
    <p class="mt-2 [&:not(:first-child)]:mt-2 font-medium">Vitepress</p>
  </linked-card>

  <linked-card href="/docs/dark-mode/astro">
    <svg
      role="img"
      viewBox="0 0 64 79"
      xmlns="http://www.w3.org/2000/svg"
      class="w-10 h-10"
      fill="currentColor"
    >
      <path d="M19.9924 65.9282C16.1165 62.432 14.9851 55.0859 16.5999 49.7638C19.3998 53.1193 23.2793 54.1822 27.2977 54.7822C33.5013 55.7081 39.5937 55.3618 45.3565 52.5637C46.0158 52.2434 46.625 51.8174 47.3454 51.386C47.8861 52.9341 48.0268 54.497 47.838 56.0877C47.3787 59.9617 45.4251 62.9542 42.3177 65.2227C41.0752 66.13 39.7604 66.9411 38.4771 67.7967C34.5346 70.4262 33.4679 73.5095 34.9494 77.9946C34.9846 78.1038 35.0161 78.2131 35.0957 78.4797C33.0828 77.5909 31.6124 76.2965 30.4921 74.5946C29.3088 72.7984 28.7458 70.8114 28.7162 68.6615C28.7014 67.6152 28.7014 66.5597 28.5588 65.5282C28.2107 63.0135 27.0144 61.8876 24.7608 61.8227C22.4479 61.7561 20.6183 63.1672 20.1331 65.3893C20.0961 65.5597 20.0424 65.7282 19.9887 65.9263L19.9924 65.9282Z" />
      <path d="M0.5 51.3932C0.5 51.3932 11.0979 46.2433 21.7254 46.2433L29.7382 21.5069C30.0381 20.3106 30.9141 19.4977 31.9029 19.4977C32.8918 19.4977 33.7677 20.3106 34.0677 21.5069L42.0804 46.2433C54.6672 46.2433 63.3058 51.3932 63.3058 51.3932C63.3058 51.3932 45.3044 2.47586 45.2692 2.37772C44.7526 0.931458 43.8804 0 42.7045 0H21.1032C19.9273 0 19.0903 0.931458 18.5384 2.37772C18.4995 2.47401 0.5 51.3932 0.5 51.3932Z" />
    </svg>
    <p class="mt-2 [&:not(:first-child)]:mt-2 font-medium">Astro</p>
  </linked-card>
</div>

---

### Apps V4 Content Docs 06.Cli (`apps/v4/content/docs/06.cli.md`)

---
title: CLI
description: Use the shadcn-vue CLI to add components to your project.
---

## init

Use the `init` command to initialize configuration and dependencies for a new project.

The `init` command installs dependencies, adds the `cn` util and configures CSS variables for the project.

```bash
npx shadcn-vue@latest init
```

**Options**

```
/* Detailed source-code truncated for AI context efficiency. */
```

The `create` command is an alias for `init`:

```bash
npx shadcn-vue@latest create
```

---

## add

Use the `add` command to add components and dependencies to your project.

```bash
npx shadcn-vue@latest add [component]
```

A component can be named in any of these ways:

| Form | Resolves to |
| --- | --- |
| `button` | an item in the default `shadcn-vue` registry |
| `@acme/button` | an item in a registry configured under `registries` in `components.json` |
| `https://acme.com/r/button.json` | a registry item fetched directly from that URL |
| `./button.json` | a registry item on disk |
| `owner/repo/button` | an item in the root `registry.json` of a public GitHub repository |
| `owner/repo/button#v1.0.0` | the same, pinned to a branch, tag or commit |

See [Install from GitHub](/docs/registry/getting-started#install-from-github) for
the GitHub form.

**Options**

```bash
Usage: shadcn-vue add [options] [components...]

add a component to your project

Arguments:
  components           names, url or local path to component

Options:
  -y, --yes            skip confirmation prompt. (default: false)
  -o, --overwrite      overwrite existing files. (default: false)
  -c, --cwd <cwd>      the working directory. defaults to the current directory.
  -a, --all            add all available components (default: false)
  -p, --path <path>    the path to add the component to.
  -s, --silent         mute output. (default: false)
  -h, --help           display help for command
```

---

## apply

Use the `apply` command to apply a preset to an existing project.

```bash
npx shadcn-vue@latest apply --preset nova
```

**Options**

```bash
Usage: shadcn-vue apply [options] [preset]

apply a preset to an existing project

Arguments:
  preset             the preset to apply

Options:
  --preset <preset>  preset configuration to apply
  -y, --yes          skip confirmation prompt. (default: false)
  -c, --cwd <cwd>    the working directory. defaults to the current directory.
  -s, --silent       mute output. (default: false)
  -h, --help         display help for command
```

---

## view

Use the `view` command to view items from the registry before installing them.

```bash
npx shadcn-vue@latest view [item]
```

You can view multiple items at once:

```bash
npx shadcn-vue@latest view button card dialog
```

Or view items from namespaced registries:

```bash
npx shadcn-vue@latest view @acme/auth @v0/dashboard
```

**Options**

```bash
Usage: shadcn-vue view [options] <items...>

view items from the registry

Arguments:
  items            the item names or URLs to view

Options:
  -c, --cwd <cwd>  the working directory. defaults to the current directory.
  -h, --help       display help for command
```

---

## search

Use the `search` command to search for items from registries.

```bash
npx shadcn-vue@latest search [registry]
```

You can search with a query:

```bash
npx shadcn-vue@latest search @shadcn-vue -q "button"
```

Or search multiple registries at once:

```bash
npx shadcn-vue@latest search @shadcn-vue @v0 @acme
```

The `list` command is an alias for `search`:

```bash
npx shadcn-vue@latest list @acme
```

**Options**

```bash
Usage: shadcn-vue search|list [options] <registries...>

search items from registries

Arguments:
  registries             the registry names or urls to search items from. Names
                         must be prefixed with @.

Options:
  -c, --cwd <cwd>        the working directory. defaults to the current directory.
  -q, --query <query>    query string
  -l, --limit <number>   maximum number of items to display per registry (default: "100")
  -o, --offset <number>  number of items to skip (default: "0")
  -h, --help             display help for command
```

---

## build

Use the `build` command to generate the registry JSON files.

```bash
npx shadcn-vue@latest build
```

This command reads the `registry.json` file and generates the registry JSON files in the `public/r` directory.

**Options**

```bash
Usage: shadcn-vue build [options] [registry]

build components for a shadcn-vue registry

Arguments:
  registry             path to registry.json file (default: "./registry.json")

Options:
  -o, --output <path>  destination directory for json files (default: "./public/r")
  -c, --cwd <cwd>      the working directory. defaults to the current directory.
  -h, --help           display help for command
```

To customize the output directory, use the `--output` option.

```bash
npx shadcn-vue@latest build --output ./public/registry
```

---

## docs

Use the `docs` command to fetch documentation and API references for components.

```bash
npx shadcn-vue@latest docs [component]
```

**Options**

```bash
Usage: shadcn-vue docs [options] <components...>

get docs, api references and usage examples for components

Arguments:
  components         component names

Options:
  -c, --cwd <cwd>    the working directory. defaults to the current directory.
  -b, --base <base>  the base to use (reka). defaults to project base.
  --json             output as JSON. (default: false)
  -h, --help         display help for command
```

---

## info

Use the `info` command to get information about your project.

```bash
npx shadcn-vue@latest info
```

**Options**

```bash
Usage: shadcn-vue info [options]

get information about your project

Options:
  -c, --cwd <cwd>  the working directory. defaults to the current directory.
  --json            output as JSON. (default: false)
  -h, --help        display help for command
```

---

## migrate

Use the `migrate` command to run migrations on your project.

```bash
npx shadcn-vue@latest migrate [migration]
```

**Available Migrations**

| Migration | Description                                             |
| --------- | ------------------------------------------------------- |
| `icons`   | Migrate your UI components to a different icon library. |
| `rtl`     | Migrate your components to support RTL (right-to-left). |

**Options**

```bash
Usage: shadcn-vue migrate [options] [migration] [path]

run a migration.

Arguments:
  migration        the migration to run.
  path             optional path or glob pattern to migrate.

Options:
  -c, --cwd <cwd>  the working directory. defaults to the current directory.
  -l, --list       list all migrations. (default: false)
  -y, --yes        skip confirmation prompt. (default: false)
  -h, --help       display help for command
```

---

### migrate rtl

The `rtl` migration transforms your components to support RTL (right-to-left) languages.

```bash
npx shadcn-vue@latest migrate rtl
```

This will:

1. Update `components.json` to set `rtl: true`
2. Transform physical CSS properties to logical equivalents (e.g., `ml-4` → `ms-4`, `text-left` → `text-start`)
3. Add `rtl:` variants where needed (e.g., `space-x-4` → `space-x-4 rtl:space-x-reverse`)

**Migrate specific files**

You can migrate specific files or use glob patterns:

```bash
# Migrate a specific file
npx shadcn-vue@latest migrate rtl src/components/ui/button/Button.vue

# Migrate files matching a glob pattern
npx shadcn-vue@latest migrate rtl "src/components/ui/**"
```

If no path is provided, the migration will transform all files in your `ui` directory (from `components.json`).

---

### Apps V4 Content Docs 07.Javascript (`apps/v4/content/docs/07.javascript.md`)

---
title: JavaScript
description: How to use shadcn-vue with JavaScript
---

This project and the components are written in TypeScript. We recommend using TypeScript for your project as well.

However we provide a JavaScript version of the components as well. The JavaScript version is available via the [cli](/docs/cli).

To opt-out of TypeScript, you can use the `typescript` flag in your `components.json` file.

```json {9} title="components.json" showLineNumbers
{
  "style": "default",
  "tailwind": {
    "config": "tailwind.config.js",
    "css": "src/app/globals.css",
    "baseColor": "zinc",
    "cssVariables": true
  },
  "typescript": false,
  "aliases": {
    "utils": "~/lib/utils",
    "components": "~/components"
  }
}
```

To configure import aliases, you can use the following `jsconfig.json`:

```json {4} title="jsconfig.json" showLineNumbers
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./*"]
    }
  }
}
```

---

### Apps V4 Content Docs 08.Rtl (`apps/v4/content/docs/08.rtl.md`)

---
title: RTL
description: Right-to-left support for shadcn-vue components.
---

shadcn-vue components have first-class support for right-to-left (RTL) layouts. Text alignment, positioning, and directional styles automatically adapt for languages like Arabic, Hebrew, and Persian.

When you install components, the CLI automatically transforms physical positioning classes to logical equivalents, so your components work seamlessly in both LTR and RTL contexts.

## Get Started

<Steps>

### Enable RTL

Create a new project using the `--rtl` flag:

```bash
npx shadcn-vue@latest create --rtl
```

This will create a `components.json` file with the `rtl: true` flag.

```json title="components.json" showLineNumbers {4}
{
  "$schema": "https://shadcn-vue.com/schema.json",
  "style": "nova",
  "rtl": true
}
```

For an existing project, set `rtl: true` in your `components.json` and see [Migrating existing components](#migrating-existing-components) below.

### Set the direction

Add the `dir="rtl"` and `lang="ar"` attributes to the `html` tag. Update `lang="ar"` to your target language.

For Vite, set them in your `index.html`:

```html title="index.html" showLineNumbers {2}
<!doctype html>
<html lang="ar" dir="rtl">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
```

For Nuxt, set them via `useHead` in your `app.vue`:

```vue title="app.vue" showLineNumbers
<script setup lang="ts">
useHead({
  htmlAttrs: {
    lang: 'ar',
    dir: 'rtl',
  },
})
</script>
```

### Add ConfigProvider

Wrap your app with the [`ConfigProvider`](https://reka-ui.com/docs/utilities/config-provider) component from `reka-ui` with the `dir="rtl"` prop, so all components read the correct direction:

```vue title="App.vue" showLineNumbers {2,6,10}
<script setup lang="ts">
import { ConfigProvider } from 'reka-ui'
</script>

<template>
  <ConfigProvider dir="rtl">
    <RouterView />
  </ConfigProvider>
</template>
```

### Add Font

For the best RTL experience, we recommend using fonts that have proper support for your target language. [Noto](https://fonts.google.com/noto) is a great font family for this and it pairs well with Inter and Geist.

Install the font using [Fontsource](https://fontsource.org/fonts/noto-sans-arabic):

```bash
npm install @fontsource-variable/noto-sans-arabic
```

Import the font in your css file:

```css title="src/assets/index.css" showLineNumbers {3,6}
@import "tailwindcss";
@import "tw-animate-css";
@import "@fontsource-variable/noto-sans-arabic";

@theme inline {
  --font-sans: "Noto Sans Arabic Variable", sans-serif;
}
```

For other languages, eg. Hebrew, you can use `@fontsource-variable/noto-sans-hebrew`.

### Add Components

You are now ready to add components to your project. The CLI will take care of handling RTL support for you.

```bash
npx shadcn-vue@latest add card
```

</Steps>

## How it works

When you add components with `rtl: true` set in your `components.json`, the CLI automatically transforms classes to be RTL compatible:

- Physical positioning classes like `left-*` and `right-*` are converted to logical equivalents like `start-*` and `end-*`.
- Text alignment and spacing classes are adjusted accordingly.
- Supported icons are automatically flipped using `rtl:rotate-180`.

## Font Recommendations

For the best RTL experience, we recommend using fonts that have proper support for your target language. [Noto](https://fonts.google.com/noto) is a great font family for this and it pairs well with Inter and Geist.

See the [Get Started](#get-started) section for details on installing and configuring RTL fonts.

## Animations

The CLI also handles animation classes, automatically transforming physical directional animations to their logical equivalents. For example, `slide-in-from-right` becomes `slide-in-from-end`.

This ensures animations like dropdowns, popovers, and tooltips animate in the correct direction based on the document's text direction.

**A note on tw-animate-css:**

There is a [known issue](https://github.com/Wombosvideo/tw-animate-css/issues/67) with the `tw-animate-css` library where the logical slide utilities are not working as expected. For now, make sure you pass in the `dir` attribute to portal elements.

```vue showLineNumbers
<Popover>
  <PopoverTrigger>Open</PopoverTrigger>
  <PopoverContent dir="rtl">
    <div>Content</div>
  </PopoverContent>
</Popover>
```

```vue showLineNumbers
<Tooltip>
  <TooltipTrigger>Open</TooltipTrigger>
  <TooltipContent dir="rtl">
    <div>Content</div>
  </TooltipContent>
</Tooltip>
```

## Migrating existing components

If you have existing components installed before enabling RTL, you can migrate them using the CLI as follows:

<Steps>

### Run the migrate command

```bash
npx shadcn-vue@latest migrate rtl [path]
```

`[path]` accepts a path or glob pattern to migrate. If you don't provide a path, it will migrate all the files in the `ui` directory.

```bash
# Migrate a specific file
npx shadcn-vue@latest migrate rtl src/components/ui/button/Button.vue

# Migrate files matching a glob pattern
npx shadcn-vue@latest migrate rtl "src/components/ui/**"
```

This will also update your `components.json` to set `rtl: true`. See the [CLI documentation](/docs/cli#migrate-rtl) for more details.

### Manual Migration (Optional)

The following components are not automatically migrated by the CLI and may need manual adjustments:

- [Calendar](/docs/components/calendar)
- [Pagination](/docs/components/pagination)
- [Sidebar](/docs/components/sidebar)

### Migrate Icons

Some icons like `ArrowRight` or `ChevronLeft` might need the `rtl:rotate-180` class to be flipped correctly. Add the `rtl:rotate-180` class to the icon component to flip it correctly.

```vue showLineNumbers
<ArrowRight class="rtl:rotate-180" />
```

### Add ConfigProvider

Wrap your app with the `ConfigProvider` component from `reka-ui`. See the [Get Started](#get-started) section for details.

</Steps>

---

### Apps V4 Content Docs 09.Figma (`apps/v4/content/docs/09.figma.md`)

---
title: Figma
description: Every component recreated in Figma. With customizable props, typography and icons.
---

<Callout>

  **Note:** The Figma files are contributed by the community. If you have any
  questions or feedback, please reach out to the Figma file maintainers.

</Callout>

## Paid

- [shadcn/ui kit](https://shadcndesign.com) by [ Matt Wierzbicki](https://x.com/matsugfx) - A premium, always up-to-date UI kit for Figma - shadcn/ui compatible and optimized for smooth design-to-dev handoff.

## Free

- [shadcn/ui design system](https://www.figma.com/community/file/1203061493325953101) by [Pietro Schirano](https://twitter.com/skirano) - A design companion for shadcn/ui. Each component was painstakingly crafted to perfectly match the code implementation.
- [Obra shadcn/ui](https://www.figma.com/community/file/1514746685758799870/obra-shadcn-ui) by [Obra Studio](https://obra.studio/) - Carefully crafted kit designed in the philosophy of shadcn, tracks v4, MIT licensed

---

### Apps V4 Content Docs 10.Changelog (`apps/v4/content/docs/10.changelog.md`)

---
title: Changelog
description: Latest updates and announcements.
---

## August 2026 - Typeset, Questionnaire & Upgrades

A new styling system, a new component, and two upgrades under the hood.

- [Typeset](#typeset): One CSS file that styles your rendered markdown.
- [Questionnaire](#questionnaire): Multi-step questions, one at a time.
- [Drawer](#drawer): Now built on Reka UI.
- [Data Table](#data-table): Now on TanStack Table v9.

### Typeset

You render markdown and get back plain unstyled HTML. So you style it: headings, paragraphs, lists, tables. Then you do it again for the docs. Then again for the chat app.

We ported **shadcn/typeset** to fix that. It's one CSS file that styles everything inside a `typeset` container, and it lives in your project, so you can change it whenever you need to.

We read a lot about type — scale ratios, tracking, optical sizing, measure, leading — tried exposing all of it, and it was too much. So we condensed it into three controls: **size**, **leading**, and **flow**. Heading sizes, list indents, the gap under a heading, the space around a rule: everything derives from those three. We call it rhythm.

```css
.typeset-docs {
  --typeset-font-body: var(--font-geist-sans);
  --typeset-font-heading: var(--font-geist-sans);
  --typeset-font-mono: var(--font-geist-mono);
  --typeset-size: 15px;
  --typeset-leading: 1.75;
  --typeset-flow: 1.25em;
}
```

It fits its container, so the same content scales down in a chat bubble and up in an article. It uses your theme tokens for colors, fonts, and radius. And it's built for streaming: when a new block arrives, earlier blocks don't shift their margins or borders.

Head to the [typeset builder](/typeset) to pick your fonts and rhythm, preview them on real content, and copy out the CSS. Full details in the [Typeset docs](/docs/typeset).

### Questionnaire

**Questionnaire** is a new component for asking a series of questions, one at a time: single-choice, multiple-choice, freeform, and skippable.

::component-preview
---
name: QuestionnaireDemo
class: style-nova
align: end
previewClass: min-h-[560px] p-4 sm:p-8
---
::

It handles navigation, answer registration, validation, and progress for you. Every part accepts `as` and `as-child`, so you can compose it into a card or a dialog and let the dialog keep its accessible name from the question title.

```bash
npx shadcn-vue@latest add questionnaire
```

See the [Questionnaire docs](/docs/components/questionnaire) for keyboard shortcuts, conditional questions, controlled answers, and resuming a session.

### Drawer

**This is a breaking change.** [Drawer](/docs/components/drawer) now uses Reka UI's first-class `Drawer` primitive instead of `vaul-vue`.

You can drop the `vaul-vue` dependency. The `direction` prop becomes `swipe-direction` — the direction the drawer dismisses toward, not the side it sits on — and `should-scale-background` is gone.

```vue

<Drawer direction="bottom">


<Drawer swipe-direction="down">
```

The values are `up`, `right`, `down`, and `left`, with `down` as the default — so a plain `<Drawer>` is still the bottom drawer.

Re-run the CLI to pick up the new component:

```bash
npx shadcn-vue@latest add drawer
```

We also added demos for [sides](/docs/components/drawer#sides) and [scrollable content](/docs/components/drawer#scrollable-content).

::component-preview
---
name: DrawerSides
---
::

### Data Table

The [Data Table](/docs/components/data-table) guide, the tasks and dashboard examples, and the `dashboard-01` block now use **TanStack Table v9**.

v9 is feature-based: you opt into sorting, filtering, pagination, and the rest with `tableFeatures()`, and anything you don't register is tree-shaken out of your bundle. `useVueTable` becomes `useTable`, row models move onto the features object, and the `valueUpdater` helper is gone — v9 manages that state internally.

```ts
export const features = tableFeatures({
  columnFilteringFeature,
  rowPaginationFeature,
  rowSortingFeature,
  filteredRowModel: createFilteredRowModel(),
  paginatedRowModel: createPaginatedRowModel(),
  sortedRowModel: createSortedRowModel(),
  filterFns: { includesString: filterFn_includesString },
  sortFns: { alphanumeric: sortFn_alphanumeric, text: sortFn_text },
})
```

One thing to watch: the built-in filter and sort functions have to be registered under `filterFns` and `sortFns`. If you reference one by string without registering it, it silently does nothing.

::component-preview
---
name: DataTableDemo
align: start
previewClass: items-start h-auto px-4 md:px-8
---
::

The guide has been rewritten end to end for v9.

## July 2026 - Rhea & Components for Chat Interfaces

Two big additions this month: a new style, **Rhea**, and a set of components for building chat interfaces.

### Rhea

Say hello to **Rhea**, a new style for shadcn-vue.

Rhea is the latest shadcn/ui look, mirroring the upstream `base-rhea` style as closely as possible. It keeps the rounded, soft aesthetic you already know but refines the details: denser surfaces, ring-based focus states, and subtle press interactions on buttons. Built for focused product interfaces.

Rhea is opt-in. The default style stays the same, so nothing changes for your existing projects. Head to the [customizer](/) and pick **Rhea** from the style switcher to preview it, then copy the theme into your app.

### Chat interface components

We're also releasing a new set of components for building chat interfaces: **Message Scroller**, **Message**, **Bubble**, **Attachment**, and **Marker**. Plus two new CSS utilities to round them out.

- [Message Scroller](#message-scroller): The scroll container for a conversation.
- [Message](#message): Lay out a row in the conversation.
- [Bubble](#bubble): Render the message surface.
- [Attachment](#attachment): Render files and images.
- [Marker](#marker): Render status updates and separators.

### Message Scroller

The scroll container for a conversation. It handles the parts that are easy to get wrong: anchored turns, streamed replies, saved thread restore, prepended history, jump-to-message, scroll controls, and visibility tracking.

::component-preview
---
name: MessageScrollerStreamingDemo
class: "style-luma rounded-[34px] sm:rounded-4xl"
previewClass: h-auto theme-green bg-surface dark:bg-background p-4 min-[480px]:p-8 min-[560px]:p-10 sm:px-10 sm:py-16
---
::

### Message

Message lays out a row in the conversation with avatar, alignment, header, content, footer, and grouped messages.

::component-preview
---
name: MessageDemo
previewClass: h-auto theme-blue
---
::

### Bubble

Bubble renders the message surface, with variants, alignment, reactions, links, buttons, and collapsible content.

::component-preview
---
name: BubbleVariantsDemo
previewClass: h-auto theme-blue
---
::

### Attachment

Attachment renders files and images with media, metadata, upload state, actions, and a full-card trigger.

::component-preview
---
name: AttachmentDemo
previewClass: h-auto theme-blue bg-surface dark:bg-background
---
::

### Marker

Marker renders status updates, system notes, bordered rows, and labeled separators.

::component-preview
---
name: MarkerDemo
class: style-luma
previewClass: h-auto theme-blue
---
::

### scroll-fade and shimmer

Two CSS utilities to finish off the set. [`scroll-fade`](/docs/utilities/scroll-fade) dissolves content at the edges of a scroll container, and [`shimmer`](/docs/utilities/shimmer) adds a shimmer effect to text, perfect for streaming and loading states. Both ship with the `shadcn-vue` package, so there's nothing to install after `init`.

::component-preview
---
name: ScrollFadeDemo
previewClass: h-auto
---
::

## November 2025 - Catch up

We've upgraded [shadcn-vue.com](/) to Nuxt v4 and Tailwind v4. The site now uses the upgraded `new-york` components.

We've also made some minor design updates to make the site faster and easier to navigate.

### Chart

We also refactored the [Chart](/docs/chart) component to match the original component, and stick to the API as close as possible.

## October 2025 - New Components

For this round of components, I looked at what we build every day, the boring stuff we rebuild over and over, and made reusable abstractions you can actually use.

- [Spinner](#spinner): An indicator to show a loading state.
- [Kbd](#kbd): Display a keyboard key or group of keys.
- [Button Group](#button-group): A group of buttons for actions and split buttons.
- [Input Group](#input-group): Input with icons, buttons, labels and more.
- [Field](#field): One component. All your forms.
- [Item](#item): Display lists of items, cards, and more.
- [Empty](#empty): Use this one for empty states.

### Spinner

Okay let's start with the easiest ones: **Spinner** and **Kbd**. Pretty basic. We all know what they do.

Here's how you render a spinner:

```vue
<script setup lang="ts">
import { Spinner } from '@/components/ui/spinner'
</script>

<template>
  <Spinner />
</template>
```

Here's what it looks like:

::component-preview
---
name: SpinnerBasic
class: "[&_.preview]:h-[250px] [&_pre]:!h-[250px]"
---
::

Here's what it looks like in a button:

::component-preview
---
name: SpinnerButtonDemo
class: "[&_.preview]:h-[250px] [&_pre]:!h-[250px]"
---
::

You can edit the code and replace it with your own spinner.

::component-preview
---
name: SpinnerCustomDemo
class: "[&_.preview]:h-[250px] [&_pre]:!h-[250px]"
---
::

### Kbd

Kbd is a component that renders a keyboard key.

```vue
<script setup lang="ts">
import { Kbd, KbdGroup } from '@/components/ui/kbd'
</script>

<template>
  <Kbd>Ctrl</Kbd>
</template>
```

Use `KbdGroup` to group keyboard keys together.

```vue showLineNumbers
<template>
  <KbdGroup>
    <Kbd>Ctrl</Kbd>
    <Kbd>B</Kbd>
  </KbdGroup>
</template>
```

::component-preview
---
name: KbdDemo
class: "[&_.preview]:h-[250px] [&_pre]:!h-[250px]"
---
::

You can add it to buttons, tooltips, input groups, and more.

### Button Group

I got a lot of requests for this one: Button Group. It's a container that groups related buttons together with consistent styling. Great for action groups, split buttons, and more.

::component-preview
---
name: ButtonGroupDemo
class: "[&_.preview]:h-[250px] [&_pre]:!h-[250px]"
---
::

Here's the code:

```vue showLineNumbers
<script setup lang="ts">
import { ButtonGroup } from '@/components/ui/button-group'
</script>

<template>
  <ButtonGroup>
    <Button>Button 1</Button>
    <Button>Button 2</Button>
  </ButtonGroup>
</template>
```

You can nest button groups to create more complex layouts with spacing.

```vue showLineNumbers
<template>
  <ButtonGroup>
    <ButtonGroup>
      <Button>Button 1</Button>
      <Button>Button 2</Button>
    </ButtonGroup>
    <ButtonGroup>
      <Button>Button 3</Button>
      <Button>Button 4</Button>
    </ButtonGroup>
  </ButtonGroup>
</template>
```

Use `ButtonGroupSeparator` to create split buttons. Classic dropdown pattern.

::component-preview
---
name: ButtonGroupWithDropdownMenuDemo
class: "[&_.preview]:h-[250px] [&_pre]:!h-[250px]"
---
::

You can also use it to add prefix or suffix buttons and text to inputs.

::component-preview
---
name: ButtonGroupWithSelectDemo
class: "[&_.preview]:h-[250px] [&_pre]:!h-[250px]"
---
::

```vue showLineNumbers
<template>
  <ButtonGroup>
    <ButtonGroupText>Prefix</ButtonGroupText>
    <Input placeholder="Type something here..." />
    <Button>Button</Button>
  </ButtonGroup>
</template>
```

### Input Group

Input Group lets you add icons, buttons, and more to your inputs. You know, all those little bits you always need around your inputs.

```vue showLineNumbers
<script setup lang="ts">
import { InputGroup, InputGroupAddon, InputGroupInput } from '@/components/ui/input-group'
</script>

<template>
  <InputGroup>
    <InputGroupInput placeholder="Search..." />
    <InputGroupAddon>
      <SearchIcon />
    </InputGroupAddon>
  </InputGroup>
</template>
```

Here's a preview with icons:

::component-preview
---
name: InputGroupWithIcon
class: "[&_.preview]:h-[300px] [&_pre]:!h-[300px]"
---
::

You can also add buttons to the input group.

::component-preview
---
name: InputGroupWithButton
class: "[&_.preview]:h-[300px] [&_pre]:!h-[300px]"
---
::

Or text, labels, tooltips,...

::component-preview
---
name: InputGroupWithText
class: "[&_.preview]:h-[400px] [&_pre]:!h-[400px]"
---
::

It also works with textareas so you can build really complex components with lots of knobs and dials or yet another prompt form.

::component-preview
---
name: InputGroupWithTextarea
class: "[&_.preview]:h-[450px] [&_pre]:!h-[450px]"
---
::

Oh here are some cool ones with spinners:

::component-preview
---
name: InputGroupWithSpinner
class: "[&_.preview]:h-[350px] [&_pre]:!h-[350px]"
---
::

### Field

Introducing **Field**, a component for building really complex forms. The abstraction here is beautiful.

It took me a long time to get it right but I made it work with all your form libraries: Vee Validate, TanStack Form, Bring Your Own Form.

Here's a basic field with an input:

```vue showLineNumbers
<script setup lang="ts">
import {
  Field,
  FieldDescription,
  FieldError,
  FieldLabel,
} from '@/components/ui/field'
</script>

<template>
  <Field>
    <FieldLabel html-for="username">
      Username
    </FieldLabel>
    <Input id="username" placeholder="Max Leiter" />
    <FieldDescription>
      Choose a unique username for your account.
    </FieldDescription>
  </Field>
</template>
```

::component-preview
---
name: FieldInputDemo
class: "[&_.preview]:h-[350px] [&_pre]:!h-[350px]"
---
::

It works with all form controls. Inputs, textareas, selects, checkboxes, radios, switches, sliders, you name it. Here's a full example:

::component-preview
---
name: FieldDemo
class: "[&_.preview]:h-[850px] [&_pre]:!h-[850px]"
---
::

Here are some checkbox fields:

::component-preview
---
name: FieldCheckboxDemo
class: "[&_.preview]:h-[500px] [&_pre]:!h-[500px]"
---
::

You can group fields together using `FieldGroup` and `FieldSet`. Perfect for
multi-section forms.

```vue showLineNumbers
<template>
  <FieldSet>
    <FieldLegend />
    <FieldGroup>
      <Field />
      <Field />
    </FieldGroup>
  </FieldSet>
</template>
```

::component-preview
---
name: FieldFieldsetDemo
class: "[&_.preview]:h-[500px] [&_pre]:!h-[500px]"
---
::

Making it responsive is easy. Use `orientation="responsive"` and it switches
between vertical and horizontal layouts based on container width. Done.

::component-preview
---
name: FieldResponsiveDemo
class: "[&_pre]:!h-[600px]"
previewClass: h-[700px] sm:h-[600px]
---
::

Wait here's more. Wrap your fields in `FieldLabel` to create a selectable field group. Really easy. And it looks great.

::component-preview
---
name: FieldChoiceCardDemo
class: "[&_.preview]:h-[600px] [&_pre]:!h-[600px]"
---
::

### Item

This one is a straightforward flex container that can house nearly any type of content.

I've built this so many times that I decided to create a component for it. Now I use it all the time. I use it to display lists of items, cards, and more.

Here's a basic item:

```vue showLineNumbers
<script setup lang="ts">
import {
  Item,
  ItemContent,
  ItemDescription,
  ItemMedia,
  ItemTitle,
} from '@/components/ui/item'
</script>

<template>
  <Item>
    <ItemMedia variant="icon">
      <HomeIcon />
    </ItemMedia>
    <ItemContent>
      <ItemTitle>Dashboard</ItemTitle>
      <ItemDescription>Overview of your account and activity.</ItemDescription>
    </ItemContent>
  </Item>
</template>
```

::component-preview
---
name: ItemDemo
class: "[&_.preview]:h-[300px] [&_.preview]:p-4 [&_pre]:!h-[300px]"
---
::

You can add icons, avatars, or images to the item.

::component-preview
---
name: ItemIconDemo
class: "[&_.preview]:h-[300px] [&_.preview]:p-4 [&_pre]:!h-[300px]"
---
::

::component-preview
---
name: ItemAvatarDemo
class: "[&_.preview]:h-[300px] [&_.preview]:p-4 [&_pre]:!h-[300px]"
---
::

And here's what a list of items looks like with `ItemGroup`:

::component-preview
---
name: ItemGroupDemo
class: "[&_.preview]:h-[500px] [&_.preview]:p-4 [&_pre]:!h-[500px]"
---
::

Need it as a link? Use the `asChild` prop:

```vue showLineNumbers
<template>
  <Item as-child>
    <a href="/dashboard">
      <ItemMedia variant="icon">
        <HomeIcon />
      </ItemMedia>
      <ItemContent>
        <ItemTitle>Dashboard</ItemTitle>
        <ItemDescription>Overview of your account and activity.</ItemDescription>
      </ItemContent>
    </a>
  </Item>
</template>
```

::component-preview
---
name: ItemLinkDemo
class: "[&_.preview]:h-[400px] [&_.preview]:p-4 [&_pre]:!h-[400px]"
---
::

### Empty

Okay last one: **Empty**. Use this to display empty states in your app.

Here's how you use it:

```vue showLineNumbers
<script setup lang="ts">
import {
  Empty,
  EmptyContent,
  EmptyDescription,
  EmptyMedia,
  EmptyTitle,
} from '@/components/ui/empty'
</script>

<template>
  <Empty>
    <EmptyMedia variant="icon">
      <InboxIcon />
    </EmptyMedia>
    <EmptyTitle>No messages</EmptyTitle>
    <EmptyDescription>You don't have any messages yet.</EmptyDescription>
    <EmptyContent>
      <Button>Send a message</Button>
    </EmptyContent>
  </Empty>
</template>
```

::component-preview
---
name: EmptyDemo
class: "[&_.preview]:h-[400px] [&_.preview]:p-4 [&_pre]:!h-[400px]"
---
::

You can use it with avatars:

::component-preview
---
name: EmptyAvatarDemo
class: "[&_.preview]:h-[400px] [&_pre]:!h-[400px]"
---
::

Or with input groups for things like search results or email subscriptions:

::component-preview
---
name: EmptyInputGroupDemo
class: "[&_.preview]:h-[450px] [&_pre]:!h-[450px]"
---
::

That's it. Seven new components. Works with all your libraries. Ready for your projects.

---

## February 2025 - Reka UI & npx shadcn-vue@latest init

We've updated the latest registry to support Reka UI instead of Radix Vue.

The updated CLI is now available. You can now install components, themes, composables, utils and more using `npx shadcn-vue add`.

This is a major step towards distributing code that you and your LLMs can access and use.

::callout
With the released of [Reka UI v2](https://reka-ui.com/), `shadcn-vue@latest` command will now install Reka UI. If you want to keep using [Radix Vue](https://radix-vue.com/), please visit [here](https://radix.shadcn-vue.com/) and run `shadcn-vue@radix` command instead.
::

1. First up, when you init into a new app, we update your existing Tailwind files instead of overriding.
2. A component now ship its own dependencies. Take the accordion for example, it can define its Tailwind keyframes. When you add it to your project, we’ll update your tailwind.config.ts file accordingly.
3. You can also install remote components using url. `npx shadcn-vue add https://acme.com/registry/navbar.json`.

4. We have created a new schema that you can use to ship your own component registry. And since it has support for urls, you can even use it to distribute private components.
5. And a few more updates like better error handling and monorepo support.

You can try the new cli today.

```bash
npx shadcn-vue@latest init Sidebar01 Login01
```

---

### Apps V4 Content Docs 11.Legacy (`apps/v4/content/docs/11.legacy.md`)

---
title: Legacy Docs
description: View the legacy docs for shadcn-vue and Tailwind v3.
---

You're looking at the docs for shadcn-vue + Tailwind v4. If you're looking for the docs for shadcn/ui + Tailwind v3, you can find them [here](https://v3.shadcn-vue.com).

<button-a to="https://v3.shadcn-vue.com" target="_blank">
  View the legacy docs
</button-a>

---

### Apps V4 Content Docs Forms (`apps/v4/content/docs/forms.md`)

---
title: Forms
description: Build forms with Vue and shadcn-vue.
---

## Pick Your Framework

Start by selecting your framework. Then follow the instructions to learn how to build forms with shadcn-vue and the form library of your choice.

<div class="mt-8 grid gap-4 sm:grid-cols-2 sm:gap-6">
  <linked-card href="/docs/forms/vee-validate">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 687.36 595.28005" class="size-10 fill-current">
      <path d="m572.4 0-57.49 99.56-171.23 296.59L172.45 99.56h118.02l53.21 92.14 53.21-92.14L454.36 0H0l343.68 595.28L687.36 0Z"/>
    </svg>
    <p class="mt-2 font-medium">VeeValidate</p>
  </linked-card>
  <linked-card href="/docs/forms/tanstack-form">
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 24 24"
      class="size-10"
      fill="currentColor"
    >
      <path d="M6.93 13.688a.343.343 0 0 1 .468.132l.063.106c.48.851.98 1.66 1.5 2.426a35.65 35.65 0 0 0 2.074 2.742.345.345 0 0 1-.039.484l-.074.066c-2.543 2.223-4.191 2.665-4.953 1.333-.746-1.305-.477-3.672.808-7.11a.344.344 0 0 1 .153-.18ZM17.75 16.3a.34.34 0 0 1 .395.27l.02.1c.628 3.286.187 4.93-1.325 4.93-1.48 0-3.36-1.402-5.649-4.203a.327.327 0 0 1-.074-.222c0-.188.156-.34.344-.34h.121a32.984 32.984 0 0 0 2.809-.098c1.07-.086 2.191-.23 3.359-.437zm.871-6.977a.353.353 0 0 1 .445-.21l.102.034c3.262 1.11 4.504 2.332 3.719 3.664-.766 1.305-2.993 2.254-6.684 2.848a.362.362 0 0 1-.238-.047.343.343 0 0 1-.125-.476l.062-.106a34.07 34.07 0 0 0 1.367-2.523c.477-.989.93-2.051 1.352-3.184zM7.797 8.34a.362.362 0 0 1 .238.047.343.343 0 0 1 .125.476l-.062.106a34.088 34.088 0 0 0-1.367 2.523c-.477.988-.93 2.051-1.352 3.184a.353.353 0 0 1-.445.21l-.102-.034C1.57 13.742.328 12.52 1.113 11.188 1.88 9.883 4.106 8.934 7.797 8.34Zm5.281-3.984c2.543-2.223 4.192-2.664 4.953-1.332.746 1.304.477 3.671-.808 7.109a.344.344 0 0 1-.153.18.343.343 0 0 1-.468-.133l-.063-.106a34.64 34.64 0 0 0-1.5-2.426 35.65 35.65 0 0 0-2.074-2.742.345.345 0 0 1 .039-.484ZM7.285 2.274c1.48 0 3.364 1.402 5.649 4.203a.349.349 0 0 1 .078.218.348.348 0 0 1-.348.344l-.117-.004a34.584 34.584 0 0 0-2.809.102 35.54 35.54 0 0 0-3.363.437.343.343 0 0 1-.394-.273l-.02-.098c-.629-3.285-.188-4.93 1.324-4.93Zm2.871 5.812h3.688a.638.638 0 0 1 .55.316l1.848 3.22a.644.644 0 0 1 0 .628l-1.847 3.223a.638.638 0 0 1-.551.316h-3.688a.627.627 0 0 1-.547-.316L7.758 12.25a.644.644 0 0 1 0-.629L9.61 8.402a.627.627 0 0 1 .546-.316Zm3.23.793a.638.638 0 0 1 .552.316l1.39 2.426a.644.644 0 0 1 0 .629l-1.39 2.43a.638.638 0 0 1-.551.316h-2.774a.627.627 0 0 1-.546-.316l-1.395-2.43a.644.644 0 0 1 0-.629l1.395-2.426a.627.627 0 0 1 .546-.316Zm-.491.867h-1.79a.624.624 0 0 0-.546.316l-.899 1.56a.644.644 0 0 0 0 .628l.899 1.563a.632.632 0 0 0 .547.316h1.789a.632.632 0 0 0 .547-.316l.898-1.563a.644.644 0 0 0 0-.629l-.898-1.558a.624.624 0 0 0-.547-.317Zm-.477.828c.227 0 .438.121.547.317l.422.73a.625.625 0 0 1 0 .629l-.422.734a.627.627 0 0 1-.547.317h-.836a.632.632 0 0 1-.547-.317l-.422-.734a.625.625 0 0 1 0-.629l.422-.73a.632.632 0 0 1 .547-.317zm-.418.817a.548.548 0 0 0-.473.273.547.547 0 0 0 0 .547.544.544 0 0 0 .473.27.544.544 0 0 0 .473-.27.547.547 0 0 0 0-.547.548.548 0 0 0-.473-.273Zm-4.422.546h.98M18.98 7.75c.391-1.895.477-3.344.223-4.398-.148-.63-.422-1.137-.84-1.508-.441-.39-1-.582-1.625-.582-1.035 0-2.12.472-3.281 1.367a14.9 14.9 0 0 0-1.473 1.316 1.206 1.206 0 0 0-.136-.144c-1.446-1.285-2.66-2.082-3.7-2.39-.617-.184-1.195-.2-1.722-.024-.559.187-1.004.574-1.317 1.117-.515.894-.652 2.074-.46 3.527.078.59.214 1.235.402 1.934a1.119 1.119 0 0 0-.215.047C3.008 8.62 1.71 9.269.926 10.015c-.465.442-.77.938-.883 1.481-.113.578 0 1.156.312 1.7.516.894 1.465 1.597 2.817 2.155.543.223 1.156.426 1.844.61a1.023 1.023 0 0 0-.07.226c-.391 1.891-.477 3.344-.223 4.395.148.629.425 1.14.84 1.508.44.39 1 .582 1.625.582 1.035 0 2.12-.473 3.28-1.364.477-.37.973-.816 1.489-1.336a1.2 1.2 0 0 0 .195.227c1.446 1.285 2.66 2.082 3.7 2.39.617.184 1.195.2 1.722.024.559-.187 1.004-.574 1.317-1.117.515-.894.652-2.074.46-3.527a14.941 14.941 0 0 0-.425-2.012 1.225 1.225 0 0 0 .238-.047c1.828-.61 3.125-1.258 3.91-2.004.465-.441.77-.937.883-1.48.113-.578 0-1.157-.313-1.7-.515-.894-1.464-1.597-2.816-2.156a14.576 14.576 0 0 0-1.906-.625.865.865 0 0 0 .059-.195z" />
    </svg>
    <p class="mt-2 font-medium">TanStack Form</p>
  </linked-card>
  <linked-card href="/docs/forms/formisch">
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 512 512"
      class="size-10"
      fill="currentColor"
    >
      <path
        fill="none"
        stroke="currentColor"
        stroke-linecap="round"
        stroke-linejoin="round"
        stroke-width="20"
        d="M90.9 291.7 49 299.4v125.5l193.3 63.8 221-63.8V304.7l-62-13.7m-352.2 8.6 193.2 53.6V488m221-183.2-221.1 48.4m-171-67.7 147.5 47 212.7-48.3L437 76 271.9 25 72.1 66.3Zm1-219.3L220.5 118l-1.8 214m1.8-214.3 216.2-41.6m-84.4 159.6-.9 1.9c-5.2 11.3-16.2 34.8-33.5 35.5h-1a22 22 0 0 1-9.7-2.2 26 26 0 0 1-7.4-5.5c-4.3-4.4-7.4-10-10-14.5q-1.2-2.4-2.7-4.7a54 54 0 0 0 21.7 4.1 74 74 0 0 0 23.6-4 70 70 0 0 0 11.4-5 64 64 0 0 0 8.5-5.6M247.6 168c19.5-20.8 34.8-12.3 36-18.4 1.5-7-12.2-7.7-22.2-2.6s-18 17.7-13.8 21m101.2-33.2c1.6 2.6 8.7-1.6 19.5-1.2s15.8 5.7 18 4.1-4.6-14.7-17.1-15.2-22 9.7-20.4 12.3m21.2 16.3c9.4.6 17.1 12.8 16.1 27.6s-10.4 25.9-19.8 25.2-17.1-12.9-16.1-27.7 10.4-25.8 19.8-25.1m-97.4 19c11 1.2 19 15.1 17.4 30.5s-12.3 27.4-23.4 26.2-19-15-17.4-30.5 12.3-27.3 23.4-26.2"
      />
    </svg>
    <p class="mt-2 font-medium">Formisch</p>
  </linked-card>
  <linked-card href="#" class="border border-dashed bg-transparent">
  <svg xmlns="http://www.w3.org/2000/svg" class="size-10" viewBox="0 0 24 24"><path fill="currentColor" d="M19.114 2H15l-3 4.9L9.429 2H0l12 21L24 2zM3 3.75h2.914L12 14.6l6.086-10.85H21L12 19.5z"/></svg>
    <p class="mt-2 font-medium">Composable</p>
    <p class="text-muted-foreground mt-1 text-xs">(Coming Soon)</p>
  </linked-card>
</div>

---

### Apps V4 Content Docs Mcp (`apps/v4/content/docs/mcp.md`)

---
title: MCP Server
description: Use the shadcn MCP server to browse, search, and install components from registries.
---

The shadcn MCP Server allows AI assistants to interact with items from registries. You can browse available components, search for specific ones, and install them directly into your project using natural language.

For example, you can ask an AI assistant to "Build a landing page using components from the acme registry" or "Find me a login form from the shadcn registry".

Registries are configured in your project's `components.json` file.

```json  title="components.json" showLineNumbers
{
  "registries": {
    "@acme": "https://acme.com/r/{name}.json"
  }
}
```

---

## Quick Start

Select your MCP client and follow the instructions to configure the shadcn MCP server. If you'd like to do it manually, see the [Configuration](#configuration) section.

::::tabs{default-value="claude" class="mt-4"}

:::tabs-list

  ::tabs-trigger{value="claude"}
  Claude Code
  ::

  ::tabs-trigger{value="cursor"}
  Cursor
  ::

  ::tabs-trigger{value="vscode"}
  VS Code
  ::

   ::tabs-trigger{value="codex"}
  Codex
  ::

    ::tabs-trigger{value="opencode"}
    Opencode
    ::

:::

::tabs-content{value="claude"}
**Run the following command** in your project:

```bash
npx shadcn-vue@latest mcp init --client claude
```

**Restart Claude Code** and try the following prompts:
- Show me all available components in the shadcn registry
- Add the button, dialog and card components to my project
- Create a contact form using components from the shadcn registry

**Note:** You can use `/mcp` command in Claude Code to debug the MCP server.
::

::tabs-content{value="cursor"}
**Run the following command** in your project:

```bash
npx shadcn-vue@latest mcp init --client cursor
```

Open **Cursor Settings** and **Enable the MCP server** for shadcn. Then try the following prompts:
- Show me all available components in the shadcn registry
- Add the button, dialog and card components to my project
- Create a contact form using components from the shadcn registry
::

::tabs-content{value="vscode"}
**Run the following command** in your project:

```bash
npx shadcn-vue@latest mcp init --client vscode
```

Open `.vscode/mcp.json` and click **Start** next to the shadcn server. Then try the following prompts with GitHub Copilot:
- Show me all available components in the shadcn registry
- Add the button, dialog and card components to my project
- Create a contact form using components from the shadcn registry
::

:::tabs-content{value="codex"}
  ::callout{type="warning"}
  **Note:** The `shadcn-vue` CLI cannot automatically update `~/.codex/config.toml`. You'll need to add the configuration manually for Codex.
  ::

**Run the following command** in your project:

```bash
npx shadcn-vue@latest mcp init --client codex
```

**Then, add the following configuration** to `~/.codex/config.toml`:

```json title=".mcp.json" showLineNumbers
[mcp_servers.shadcn]
command = "npx"
args = ["shadcn-vue@latest", "mcp"]
```

**Restart Codex** and try the following prompts:
- Show me all available components in the shadcn registry
- Add the button, dialog and card components to my project
- Create a contact form using components from the shadcn registry
:::
::tabs-content{value="opencode"}

You need to make sure opencode is installed. 

To install check [Opencode Documentation](https://opencode.ai/)

**Run the following command** in your project:
```bash
npx shadcn-vue@latest mcp init --client opencode
```
**Restart opencode** and try the following prompts:
- Show me all available components in the shadcn registry
- Add the button, dialog and card components to my project
- Create a contact form using components from the shadcn registry

**Note:** You can use `Ctrl+x s` to see the status of the mcp servers.
::

::::

---

## What is MCP?

[Model Context Protocol (MCP)](https://modelcontextprotocol.io) is an open protocol that enables AI assistants to securely connect to external data sources and tools. With the shadcn MCP server, your AI assistant gains direct access to:

- **Browse Components** - List all available components, blocks, and templates from any configured registry
- **Search Across Registries** - Find specific components by name or functionality across multiple sources
- **Install with Natural Language** - Add components using simple conversational prompts like "add a login form"
- **Support for Multiple Registries** - Access public registries, private company libraries, and third-party sources

---

## How It Works

The MCP server acts as a bridge between your AI assistant, component registries and the shadcn CLI.

1. **Registry Connection** - MCP connects to configured registries (shadcn/ui, private registries, third-party sources)
2. **Natural Language** - You describe what you need in plain English
3. **AI Processing** - The assistant translates your request into registry commands
4. **Component Delivery** - Resources are fetched and installed in your project

---

## Supported Registries

The shadcn MCP server works out of the box with any shadcn-compatible registry.

- **shadcn/ui Registry** - The default registry with all shadcn/ui components
- **Third-Party Registries** - Any registry following the shadcn registry specification
- **Private Registries** - Your company's internal component libraries
- **Namespaced Registries** - Multiple registries configured with `@namespace` syntax

---

## Configuration

You can use any MCP client to interact with the shadcn MCP server. Here are the instructions for the most popular ones.

### Claude Code

To use the shadcn MCP server with Claude Code, add the following configuration to your project's `.mcp.json` file:

```json title=".mcp.json" showLineNumbers
{
  "mcpServers": {
    "shadcn": {
      "command": "npx",
      "args": ["shadcn-vue@latest", "mcp"]
    }
  }
}
```

After adding the configuration, restart Claude Code and run `/mcp` to see the shadcn MCP server in the list. If you see `Connected`, you're good to go.

See the [Claude Code MCP documentation](https://docs.anthropic.com/en/docs/claude-code/mcp) for more details.

### Cursor

To configure MCP in Cursor, add the shadcn server to your project's `.cursor/mcp.json` configuration file:

```json title=".cursor/mcp.json" showLineNumbers
{
  "mcpServers": {
    "shadcn": {
      "command": "npx",
      "args": ["shadcn-vue@latest", "mcp"]
    }
  }
}
```

After adding the configuration, enable the shadcn MCP server in Cursor Settings.

Once enabled, you should see a green dot next to the shadcn server in the MCP server list and a list of available tools.

See the [Cursor MCP documentation](https://docs.cursor.com/en/context/mcp#using-mcp-json) for more details.

### VS Code

To configure MCP in VS Code with GitHub Copilot, add the shadcn server to your project's `.vscode/mcp.json` configuration file:

```json title=".vscode/mcp.json" showLineNumbers
{
  "mcpServers": {
    "shadcn": {
      "command": "npx",
      "args": ["shadcn-vue@latest", "mcp"]
    }
  }
}
```

After adding the configuration, open `.vscode/mcp.json` and click **Start** next to the shadcn server.

See the [VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more details.

### Codex

::callout{type="warning"}
**Note:** The `shadcn-vue` CLI cannot automatically update `~/.codex/config.toml`. You'll need to add the configuration manually.
::

To configure MCP in Codex, add the shadcn server to `~/.codex/config.toml`:

```toml title="~/.codex/config.toml" showLineNumbers
[mcp_servers.shadcn]
command = "npx"
args = ["shadcn-vue@latest", "mcp"]
```

After adding the configuration, restart Codex to load the MCP server.

### Opencode

To configure MCP in opencode add the shadcn server to `opencode.json`:

```json title="opencode.json" showLineNumbers
{
    "$schema": "https://opencode.ai/config.json",
    "mcp": {
        "shadcnVue": {
            "type": "local",
            "enabled": true,
            "command": ["npx", "shadcn-vue@latest", "mcp"]
        }
    }
}
```
See the [Opencode Documentation](https://opencode.ai/docs/mcp-servers/) for more details.

---

## Configuring Registries

The MCP server supports multiple registries through your project's `components.json` configuration. This allows you to access components from various sources including private registries and third-party providers.

Configure additional registries in your `components.json`:

```json title="components.json" showLineNumbers
{
  "registries": {
    "@acme": "https://registry.acme.com/{name}.json",
    "@internal": {
      "url": "https://internal.company.com/{name}.json",
      "headers": {
        "Authorization": "Bearer ${REGISTRY_TOKEN}"
      }
    }
  }
}
```

::callout{type="info"}
**Note:** No configuration is needed to access the standard shadcn/ui registry.
::

---

## Authentication

For private registries requiring authentication, set environment variables in your `.env.local`:

```bash title=".env.local"
REGISTRY_TOKEN=your_token_here
API_KEY=your_api_key_here
```

For more details on registry authentication, see the [Authentication documentation](/docs/registry/authentication).

---

## Example Prompts

Once the MCP server is configured, you can use natural language to interact with registries. Try one of the following prompts:

### Browse & Search

- Show me all available components in the shadcn registry
- Find me a login form from the shadcn registry

### Install Items

- Add the button component to my project
- Create a login form using shadcn components
- Install the Cursor rules from the acme registry

### Work with Namespaces

- Show me components from acme registry
- Install @internal/auth-form
- Build me a landing page using hero, features and testimonials sections from the acme registry

---

## Troubleshooting

### MCP Not Responding

If the MCP server isn't responding to prompts:

1. **Check Configuration** - Verify the MCP server is properly configured and enabled in your MCP client
2. **Restart MCP Client** - Restart your MCP client after configuration changes
3. **Verify Installation** - Ensure `shadcn-vue` is installed in your project
4. **Check Network** - Confirm you can access the configured registries

### Registry Access Issues

If components aren't loading from registries:

1. **Check components.json** - Verify registry URLs are correct
2. **Test Authentication** - Ensure environment variables are set for private registries
3. **Verify Registry** - Confirm the registry is online and accessible
4. **Check Namespace** - Ensure namespace syntax is correct (`@namespace/component`)

### Installation Failures

If components fail to install:

1. **Check Project Setup** - Ensure you have a valid `components.json` file
2. **Verify Paths** - Confirm the target directories exist
3. **Check Permissions** - Ensure write permissions for component directories
4. **Review Dependencies** - Check that required dependencies are installed

### No Tools or Prompts

If you see the `No tools or prompts` message, try the following:

1. **Clear the npx cache** - Run `npx clear-npx-cache`
2. **Re-enable the MCP server** - Try to re-enable the MCP server in your MCP client
3. **Check Logs** - In Cursor, you can see the logs under View -> Output and select `MCP: project-*` in the dropdown.

---

## Learn More

- [Registry Documentation](/docs/registry) - Complete guide to shadcn registries
- [Namespaces](/docs/registry/namespace) - Configure multiple registry sources
- [Authentication](/docs/registry/authentication) - Secure your private registries
- [MCP Specification](https://modelcontextprotocol.io) - Learn about Model Context Protocol

---

### Apps V4 Content Docs Skills (`apps/v4/content/docs/skills.md`)

---
title: Skills
description: Give your AI assistant deep knowledge of shadcn-vue components, patterns, and best practices.
---

Skills give AI assistants like Claude Code project-aware context about shadcn-vue. When installed, your AI assistant knows how to find, install, compose, and customize components using the correct APIs and patterns for your project.

For example, you can ask your AI assistant to:

- _"Add a login form with email and password fields."_
- _"Create a settings page with a form for updating profile information."_
- _"Build a dashboard with a sidebar, stats cards, and a data table."_
- _"Switch to --preset [CODE]"_

The skill reads your project's `components.json` and provides the assistant with your framework, aliases, installed components, icon library, and base library so it can generate correct code on the first try.

---

## Install

```bash
npx skills add unovue/shadcn-vue
```

This installs the shadcn-vue skill into your project. Once installed, your AI assistant automatically loads it when working with shadcn-vue components.

Learn more about skills at [skills.sh](https://skills.sh).

---

## What's Included

The skill provides your AI assistant with the following knowledge:

### Project Context

On every interaction, the skill runs `shadcn-vue info --json` to get your project's configuration: framework, Tailwind version, aliases, base library (`reka`), icon library, installed components, and resolved file paths.

### CLI Commands

Full reference for all CLI commands: `init`, `add`, `search`, `view`, `docs`, `diff`, `info`, and `build`. Includes flags, smart merge workflows, presets, and templates.

### Theming and Customization

How CSS variables, OKLCH colors, dark mode, custom colors, border radius, and component variants work. Includes guidance for both Tailwind v3 and v4.

### Registry Authoring

How to build and publish custom component registries: `registry.json` format, item types, file objects, dependencies, CSS variables, building, hosting, and user configuration.

### MCP Server

Setup and tools for the shadcn-vue MCP server, which lets AI assistants search, browse, and install components from registries.

---

## How It Works

1. **Project detection** — The skill activates when it finds a `components.json` file in your project.
2. **Context injection** — It runs `shadcn-vue info --json` to read your project configuration and injects the result into the assistant's context.
3. **Pattern enforcement** — The assistant follows shadcn-vue composition rules: using `FieldGroup` for forms, `ToggleGroup` for option sets, semantic colors, and correct base-specific APIs.
4. **Component discovery** — The assistant uses `shadcn-vue docs`, `shadcn-vue search`, or MCP tools to find components and their documentation before generating code.

## Learn More

- [CLI](/docs/06.cli) — Full CLI command reference
- [MCP Server](/docs/mcp) — Connect the MCP server for registry access
- [Theming](/docs/04.theming) — CSS variables and customization
- [Registry](/docs/registry) — Building and publishing custom registries
- [skills.sh](https://skills.sh) — Learn more about AI skills

---

### Apps V4 Content Docs Typeset (`apps/v4/content/docs/typeset.md`)

---
title: Typeset
description: A styling system for HTML and rendered markdown, from blog posts to streaming chat. One CSS file you own.
---

You render markdown and get back plain unstyled HTML: headings, paragraphs, lists, and tables. So you style the elements one by one: font sizes, line heights, spacing.

You do it for your blog. Then you do it again for the docs. Then again for the chat app. Every time you're fighting the same thing: sizing and spacing.

To fix this, we ported **shadcn/typeset**. It's one CSS file that styles everything inside a `typeset` container. The file lives in your project, so you can change it directly when you need to.

A typeset is just a small preset class. You can have multiple typesets in your app, for different contexts.

```css
.typeset-docs {
  --typeset-font-body: var(--font-geist-sans);
  --typeset-font-heading: var(--font-geist-sans);
  --typeset-font-mono: var(--font-geist-mono);
  --typeset-size: 15px;
  --typeset-leading: 1.75;
  --typeset-flow: 1.25em;
}
```

[Build your typeset](/typeset)

---

## Principles

We read a lot about type: scale ratios, tracking, kerning, optical sizing, measure, leading, the space above and below every element. We tried exposing all of it, and it was too much. Nobody wants to set a dozen variables to make markdown look right.

So we sat down and condensed everything into three controls: size, leading, and flow. Everything else, heading sizes, list indents, the gap under a heading, the space around a rule, derives from them. Three controls. We called it rhythm.

---

## Features

- **It fits its container.** Put it in a chat bubble and it follows the smaller type around it. Put it in an article and it scales up with the page. On smaller screens, it gets a small bump for readability.
- **It uses your theme.** Colors, fonts, and radius come from your app. Dark mode follows the same tokens.
- **It's easy to tune.** Three values control the base size, line height, and space between blocks. Change them in a preset and the whole document follows.
- **It works well with streaming.** When a new block arrives, Typeset doesn't make earlier blocks switch margins, borders, or styles.

---

## Building Your Typeset

Create your typeset in the [typeset builder](/typeset). Pick your fonts and rhythm, then preview them on docs, chat, articles, and other real content.

The panel gives you the `typeset.css` file, the font setup for your framework, a preset class with your choices, and the wrapper to add around your content.

Copy `typeset.css` next to your main CSS file and import it after Tailwind:

```css
@import "tailwindcss";
@import "./typeset.css";
```

Then wrap your rendered markdown with `typeset` and your preset class:

```vue
<template>
  <div class="typeset typeset-docs">
    <ContentRenderer :value="page" />
  </div>
</template>
```

`typeset` turns the styles on. `typeset-docs` is the preset you created in the builder.

---

## Custom Typesets

The file includes defaults, so you can use `typeset` by itself. Most of the reading rhythm comes from three values:

```css
.typeset {
  --typeset-font-body: inherit;
  --typeset-font-heading: var(--font-heading);
  --typeset-font-mono: var(--font-mono);

  --typeset-size: 1em; /* body font-size */
  --typeset-leading: 1.75; /* line-height */
  --typeset-flow: 1.25em; /* space between blocks */
}
```

- **`--typeset-size`** sets the base text size. `1em` follows the surrounding layout. On smaller screens, Typeset bumps it up a little.
- **`--typeset-leading`** sets the space between lines.
- **`--typeset-flow`** sets the space between blocks. Headings and other elements derive their spacing from it.

The font variables tell Typeset which families to use. Leave them alone and it follows your app. Colors and radius come from your theme too.

Typeset doesn't set a maximum width. Your layout owns that. The Measure control in the builder adds a `max-width` to the wrapper instead of hiding it in the stylesheet.

You can keep more than one preset in the same app. Here is a tighter one for chat and a roomier one for docs:

```css
.typeset-chat {
  --typeset-flow: 1em;
  --typeset-leading: 1.6;
}

.typeset-docs {
  --typeset-size: 15px;
  --typeset-flow: 1.5em;
}
```

```vue
<template>
  <div class="typeset typeset-chat" v-html="message" />
  <article class="typeset typeset-docs" v-html="page" />
</template>
```

For a one-off change, skip the preset and set a value on the container:

```vue
<template>
  <article class="typeset [--typeset-flow:1.75em]">...</article>
</template>
```

---

## Custom Themes

A preset can change the whole feel of the content, not just the spacing. You can give readers a serif reading mode, a compact UI mode, or any other style that fits your product.

```css
/* Reading: serif, larger type, roomy rhythm. */
.typeset-reading {
  --typeset-font-body: var(--font-lora);
  --typeset-font-heading: var(--font-lora);
  --typeset-size: 18px;
  --typeset-leading: 1.9;
  --typeset-flow: 2em;
}

/* Compact: sans, smaller type, tighter rhythm. */
.typeset-compact {
  --typeset-font-body: var(--font-geist-sans);
  --typeset-font-heading: var(--font-geist-sans);
  --typeset-size: 14px;
  --typeset-leading: 1.6;
  --typeset-flow: 1em;
}
```

---

## Accessibility and Dark Mode

For readers who prefer larger type and more space, create a roomier typeset and expose it as a setting:

```css
.typeset-large {
  --typeset-size: 16px;
  --typeset-leading: 2;
  --typeset-flow: 2em;
}
```

Dark mode already follows your theme colors. If the text feels a little tight on a dark surface, you can loosen the leading there:

```css
.dark .typeset {
  --typeset-leading: 1.9;
}
```

---

## Responsive Table

Tables stay real tables and wrap to fit. To scroll a wide one horizontally instead, wrap it in `typeset-scroll`. The wrapper is styled as part of the content, so it only works *inside* a `typeset` container:

```vue
<template>
  <article class="typeset typeset-docs">
    <div class="typeset-scroll">
      <table>...</table>
    </div>
  </article>
</template>
```

Do this in your renderer's table component or a small rehype plugin, where the output already sits inside the container. It works for any wide block, not just tables.

---

## Overrides

Typeset lives in the `components` layer and uses `:where()` for its element selectors. Tailwind utilities on an element win without `!important`:

```vue
<template>
  <div class="typeset typeset-docs">
    <p class="text-lg">...</p>
  </div>
</template>
```

Plain CSS can override Typeset with a normal selector too.

---

## Opting Out

To keep a component out of Typeset, add `not-typeset` or `data-not-typeset`:

```vue
<template>
  <div class="typeset">
    <p>Styled prose.</p>
    <Card class="not-typeset">
      Untouched component.
    </Card>
  </div>
</template>
```

Both options cover the component and everything inside it. Another `typeset` container inside that subtree stays opted out too.

---

## Streaming

Typeset is written so that adding a new block does not change the styles of the blocks already on screen.

- No forward-looking selectors. `:last-child`, `:has()`, and `:empty` are left out of layout rules because their matches can change as content is added.
- Spacing flows in one direction, using `margin-block-start` only. A new block adds its own space.
- Table separators live on the cells being added, so a new row does not restyle the row above it.

Text that is still streaming can grow and wrap normally. Typeset just avoids restyling the blocks that came before it.

---

## Prior Art

The `prose` class from `@tailwindcss/typography` is excellent at what it was built for: adding beautiful typographic defaults to plain HTML, including content rendered from Markdown or a CMS.

Typeset takes a different approach with container-aware sizing, app theme tokens, presets for different contexts, and streaming stability. Here's where they differ:

|              | @tailwindcss/typography                      | Typeset                                          |
| ------------ | -------------------------------------------- | ------------------------------------------------ |
| Sizing       | Fixed `rem` scale, `prose-sm` to `prose-2xl` | Relative to the container, any size              |
| Dark mode    | `prose-invert`, a second palette             | Your tokens flip, nothing to add                 |
| Theming      | Prose color variables; scale baked in        | Your theme tokens, plus font and rhythm controls |
| Overrides    | `prose-a:`, `prose-headings:` modifier API   | Plain utilities and CSS win                      |
| Streaming    | No append-stability contract                 | Designed for stable appends                      |
| Distribution | npm plugin, generated CSS                    | One CSS file you own                             |

Typeset borrows the two best ideas from the plugin: the zero-specificity `:where()` guard pattern, and the escape-hatch class (`not-typeset`, in the spirit of `not-prose`).

---

### Apps V4 Content Docs Components Accordion (`apps/v4/content/docs/components/accordion.md`)

---
title: Accordion
description: A vertically stacked set of interactive headings that each reveal a section of content.
component: true
links:
  doc: https://reka-ui.com/docs/components/accordion
  api: https://reka-ui.com/docs/components/accordion#api-reference
---

::component-preview
---
name: AccordionDemo
class: '[&_.preview>div]:sm:max-w-[80%]'
description: An accordion with three items
align: start
previewClass: h-[27rem] sm:h-[25rem]
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add accordion
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/accordion) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger,
} from '@/components/ui/accordion'
</script>

<template>
  <Accordion type="single" collapsible>
    <AccordionItem value="item-1">
      <AccordionTrigger>Is it accessible?</AccordionTrigger>
      <AccordionContent>
        Yes. It adheres to the WAI-ARIA design pattern.
      </AccordionContent>
    </AccordionItem>
  </Accordion>
</template>
```

---

### Apps V4 Content Docs Components Alert Dialog (`apps/v4/content/docs/components/alert-dialog.md`)

---
title: Alert Dialog
description: A modal dialog that interrupts the user with important content and expects a response.
component: true
links:
  doc: https://reka-ui.com/docs/components/alert-dialog
  api: https://reka-ui.com/docs/components/alert-dialog#api-reference
---

::component-preview
---
name: AlertDialogDemo
class: '[&_.preview]:min-h-[400px]'
description: An alert dialog that asks for user confirmation
---
::

::vue-school-link{class="mt-6" lesson="dialogs-and-alerts-with-shadcn-vue" placement="top"}
Watch a Vue School video about dialogs and alerts in shadcn-vue.
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add alert-dialog
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/alert-dialog) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from '@/components/ui/alert-dialog'
</script>

<template>
  <AlertDialog>
    <AlertDialogTrigger>Open</AlertDialogTrigger>
    <AlertDialogContent>
      <AlertDialogHeader>
        <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
        <AlertDialogDescription>
          This action cannot be undone. This will permanently delete your account
          and remove your data from our servers.
        </AlertDialogDescription>
      </AlertDialogHeader>
      <AlertDialogFooter>
        <AlertDialogCancel>Cancel</AlertDialogCancel>
        <AlertDialogAction>Continue</AlertDialogAction>
      </AlertDialogFooter>
    </AlertDialogContent>
  </AlertDialog>
</template>
```

---

### Apps V4 Content Docs Components Alert (`apps/v4/content/docs/components/alert.md`)

---
title: Alert
description: Displays a callout for user attention.
component: true
---

::component-preview
---
name: AlertDemo
title: An alert with an icon, title and description.
description: An alert with an icon, title and description.
previewClass: h-auto sm:h-[22rem] p-6
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add alert
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/alert) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
</script>

<template>
  <Alert>
    <AlertTitle>Heads up!</AlertTitle>
    <AlertDescription>
      You can add components and dependencies to your app using the cli.
    </AlertDescription>
  </Alert>
</template>
```

---

### Apps V4 Content Docs Components Aspect Ratio (`apps/v4/content/docs/components/aspect-ratio.md`)

---
title: Aspect Ratio
description: Displays content within a desired ratio.
component: true
links:
  doc: https://reka-ui.com/docs/components/aspect-ratio
  api: https://reka-ui.com/docs/components/aspect-ratio#api-reference
---

::component-preview
---
name: AspectRatioDemo
description: A component that displays an image with a 16:9 aspect ratio.
previewClass: h-[26rem]
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add aspect-ratio
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/aspect-ratio) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script lang="ts">
import { AspectRatio } from '@/components/ui/aspect-ratio'
</script>

<template>
  <AspectRatio :ratio="16 / 9">
    <img src="..." alt="Image" class="rounded-md object-cover">
  </AspectRatio>
</template>
```

---

### Apps V4 Content Docs Components Attachment (`apps/v4/content/docs/components/attachment.md`)

---
title: Attachment
description: Displays a file or image attachment with media, metadata, upload state, and actions.
component: true
---

::component-preview
---
name: AttachmentDemo
previewClass: h-auto theme-blue bg-surface dark:bg-background
---
::

The `Attachment` component displays a file or image attachment, its media, name, and metadata, with optional actions and upload state. Use it for files and images in chat composers, message threads, and upload lists.


## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add attachment
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/bases/reka/ui/attachment) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::


## Usage

```vue showLineNumbers
<script setup lang="ts">
import { FileTextIcon, XIcon } from '@lucide/vue'
import {
  Attachment,
  AttachmentAction,
  AttachmentActions,
  AttachmentContent,
  AttachmentDescription,
  AttachmentMedia,
  AttachmentTitle,
} from '@/components/ui/attachment'
</script>

<template>
  <Attachment>
    <AttachmentMedia>
      <FileTextIcon />
    </AttachmentMedia>
    <AttachmentContent>
      <AttachmentTitle>sales-dashboard.pdf</AttachmentTitle>
      <AttachmentDescription>PDF · 2.4 MB</AttachmentDescription>
    </AttachmentContent>
    <AttachmentActions>
      <AttachmentAction aria-label="Remove sales-dashboard.pdf">
        <XIcon />
      </AttachmentAction>
    </AttachmentActions>
  </Attachment>
</template>
```

## Composition

Use the following composition to build an attachment:

```text
Attachment
├── AttachmentMedia
├── AttachmentContent
│   ├── AttachmentTitle
│   └── AttachmentDescription
├── AttachmentActions
│   └── AttachmentAction
└── AttachmentTrigger
```

Use `AttachmentGroup` to lay out multiple attachments in a scrollable row:

```text
AttachmentGroup
├── Attachment
└── Attachment
```

## Features

- Icon and image media through `AttachmentMedia`
- Upload states: `idle`, `uploading`, `processing`, `error`, and `done` with built-in styling and a shimmer while in progress
- Three sizes and horizontal or vertical orientation
- A full-card `AttachmentTrigger` that opens a link or dialog while the actions stay independently clickable
- Scrollable, snapping `AttachmentGroup` with an edge fade
- Customizable styling through the Vue `class` attribute on every part

## Examples

### Image

Set `variant="image"` on `AttachmentMedia` and render an `<img>` inside it. Use `orientation="vertical"` to stack the media above the content.

::component-preview
---
name: AttachmentImageDemo
previewClass: h-auto theme-blue bg-surface dark:bg-background
---
::

### States

Set `state` to reflect the upload lifecycle. `uploading` and `processing` shimmer the title, and `error` switches to a destructive treatment.

::component-preview
---
name: AttachmentStatesDemo
previewClass: h-auto theme-blue bg-surface dark:bg-background
---
::

### Sizes

Use `size` to switch between `default`, `sm`, and `xs`.

::component-preview
---
name: AttachmentSizesDemo
previewClass: h-auto theme-blue bg-surface dark:bg-background
---
::

### Group

Wrap attachments in `AttachmentGroup` to lay them out in a horizontally scrollable, snapping row with an edge fade.


::component-preview
---
name: AttachmentGroupDemo
previewClass: h-auto theme-blue bg-surface dark:bg-background
---
::

### Trigger

Add an `AttachmentTrigger` to make the whole card open a link or dialog. It fills the card behind the actions, so the actions stay clickable.

::component-preview
---
name: AttachmentTriggerDemo
previewClass: h-auto theme-blue bg-surface dark:bg-background
---
::

```vue showLineNumbers
<template>
  <Dialog>
    <Attachment>
      
      <DialogTrigger as-child>
        <AttachmentTrigger aria-label="Preview research-summary.pdf" />
      </DialogTrigger>
    </Attachment>
    <DialogContent>
      
    </DialogContent>
  </Dialog>
</template>
```

## Accessibility

`AttachmentAction` renders a `Button`, and `AttachmentTrigger` renders a real `<button>` (or your element via `as-child`). Follow the guidance below so both are operable and announced.

### Label icon-only actions

`AttachmentAction` is usually icon-only, so give each one an `aria-label` describing the action and its target.

```vue showLineNumbers
<template>
  <AttachmentAction aria-label="Remove sales-dashboard.pdf">
    <XIcon />
  </AttachmentAction>
</template>
```

### Label the trigger

`AttachmentTrigger` covers the card with no text of its own, so give it an `aria-label` for what activating it does.

```vue showLineNumbers
<template>
  <AttachmentTrigger as-child>
    <a
      :href="url"
      target="_blank"
      rel="noreferrer"
      aria-label="Open workspace.png"
    />
  </AttachmentTrigger>
</template>
```

The trigger sits behind the actions in the stacking order, so an `AttachmentAction` and the `AttachmentTrigger` never trap each other — both remain separately focusable and clickable.

### Keyboard scrolling

An `AttachmentGroup` scrolls horizontally. When its attachments are interactive: a trigger or actions, keyboard users reach off-screen items by tabbing to them. For a row of presentational attachments, make the group itself focusable and scrollable by adding `tabindex="0"`, `role="group"`, and an `aria-label`.

### Meaning beyond color

The `error` state uses a destructive color. Keep the failure reason in `AttachmentDescription` so the state is not conveyed by color alone.

## API Reference

### Attachment

The root attachment container.

| Prop          | Type                                                         | Default        | Description                                       |
| ------------- | ------------------------------------------------------------ | -------------- | ------------------------------------------------- |
| `state`       | `"idle" \| "uploading" \| "processing" \| "error" \| "done"` | `"done"`       | The upload state. Drives styling and the shimmer. |
| `size`        | `"default" \| "sm" \| "xs"`                                  | `"default"`    | The attachment size.                              |
| `orientation` | `"horizontal" \| "vertical"`                                 | `"horizontal"` | Lay the media beside or above the content.        |
| `class`       | `HTMLAttributes["class"]`                                    | -              | Additional classes to apply to the root element.  |

### AttachmentMedia

The media slot for an icon or image preview.

| Prop        | Type                | Default  | Description                                    |
| ----------- | ------------------- | -------- | ---------------------------------------------- |
| `variant`   | `"icon" \| "image"` | `"icon"` | Whether the media holds an icon or an `<img>`. |
| `class`   | `HTMLAttributes["class"]` | -        | Additional classes to apply to the media slot. |

### AttachmentContent

Wraps the title and description.

| Prop        | Type     | Default | Description                                      |
| ----------- | -------- | ------- | ------------------------------------------------ |
| `class` | `HTMLAttributes["class"]` | -       | Additional classes to apply to the content slot. |

### AttachmentTitle

The attachment name. Shimmers while the attachment is `uploading` or `processing`.

| Prop        | Type     | Default | Description                               |
| ----------- | -------- | ------- | ----------------------------------------- |
| `class` | `HTMLAttributes["class"]` | -       | Additional classes to apply to the title. |

### AttachmentDescription

Secondary metadata such as the file type, size, or upload status.

| Prop        | Type     | Default | Description                                     |
| ----------- | -------- | ------- | ----------------------------------------------- |
| `class` | `HTMLAttributes["class"]` | -       | Additional classes to apply to the description. |

### AttachmentActions

A container for one or more actions, aligned to the end of the attachment.

| Prop        | Type     | Default | Description                                 |
| ----------- | -------- | ------- | ------------------------------------------- |
| `class` | `HTMLAttributes["class"]` | -       | Additional classes to apply to the actions. |

### AttachmentAction

An action button. Renders a [`Button`](/docs/components/button) and accepts Vue fallthrough attributes such as `aria-label`.

| Prop       | Type                       | Default     | Description                                  |
| ---------- | -------------------------- | ----------- | -------------------------------------------- |
| `variant`  | `ButtonVariants["variant"]` | `"ghost"`   | The button variant.                          |
| `size`     | `ButtonVariants["size"]`   | `"icon-xs"` | The button size.                             |
| `class`    | `HTMLAttributes["class"]`  | -           | Additional classes to apply to the action.   |

### AttachmentTrigger

A full-card overlay that activates the attachment. Renders a `<button>` by default and accepts Vue fallthrough attributes such as `aria-label`.

| Prop       | Type                             | Default | Description                                  |
| ---------- | -------------------------------- | ------- | -------------------------------------------- |
| `as`       | `PrimitiveProps["as"]`           | `"button"` | Element or component to render.           |
| `as-child` | `boolean`                        | `false` | Render as the child element, such as a link. |
| `class`    | `HTMLAttributes["class"]`        | -       | Additional classes to apply to the trigger.  |

### AttachmentGroup

Lays out attachments in a horizontally scrollable, snapping row.

| Prop        | Type     | Default | Description                               |
| ----------- | -------- | ------- | ----------------------------------------- |
| `class` | `HTMLAttributes["class"]` | -       | Additional classes to apply to the group. |

---

### Apps V4 Content Docs Components Avatar (`apps/v4/content/docs/components/avatar.md`)

---
title: Avatar
description: An image element with a fallback for representing the user.
component: true
links:
  doc: https://reka-ui.com/docs/components/avatar
  api: https://reka-ui.com/docs/components/avatar#api-reference
---

::component-preview
---
name: AvatarDemo
class: '[&_.preview]:min-h-[200px]'
description: An avatar with a fallback.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add avatar
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/avatar) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
</script>

<template>
  <Avatar>
    <AvatarImage src="https://github.com/shadcn.png" />
    <AvatarFallback>CN</AvatarFallback>
  </Avatar>
</template>
```

---

### Apps V4 Content Docs Components Badge (`apps/v4/content/docs/components/badge.md`)

---
title: Badge
description: Displays a badge or a component that looks like a badge.
component: true
---

::component-preview
---
name: BadgeDemo
description: A default badge
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add badge
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/badge) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Badge } from '@/components/ui/badge'
</script>

<template>
  <Badge variant="default | outline | secondary | destructive">
    Badge
  </Badge>
</template>
```

### Link

You can use the `as-child` prop to make another component look like a badge. Here's an example of a link that looks like a badge.

```vue showLineNumbers
<script setup lang="ts">
import { Badge } from '@/components/ui/badge'
</script>

<template>
  <Badge as-child>
    <a href="#">Badge</a>
  </Badge>
</template>
```

---

### Apps V4 Content Docs Components Breadcrumb (`apps/v4/content/docs/components/breadcrumb.md`)

---
title: Breadcrumb
description: Displays the path to the current resource using a hierarchy of links.
component: true
---

::component-preview
---
name: BreadcrumbDemo
class: '[&_.preview]:p-2'
description: A breadcrumb with a collapsible dropdown.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add breadcrumb
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/breadcrumb) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from '@/components/ui/breadcrumb'
</script>

<template>
  <Breadcrumb>
    <BreadcrumbList>
      <BreadcrumbItem>
        <BreadcrumbLink href="/">
          Home
        </BreadcrumbLink>
      </BreadcrumbItem>
      <BreadcrumbSeparator />
      <BreadcrumbItem>
        <BreadcrumbLink href="/components">
          Components
        </BreadcrumbLink>
      </BreadcrumbItem>
      <BreadcrumbSeparator />
      <BreadcrumbItem>
        <BreadcrumbPage>Breadcrumb</BreadcrumbPage>
      </BreadcrumbItem>
    </BreadcrumbList>
  </Breadcrumb>
</template>
```

## Examples

### Custom separator

Use a custom component as `children` for `<BreadcrumbSeparator />` to create a custom separator.

::component-preview
---
name: BreadcrumbCustomSeparatorDemo
---
::

```vue showLineNumbers {2,12-14}
<script setup lang="ts">
import { SlashIcon } from '@lucide/vue'
// ...
</script>

<template>
  <Breadcrumb>
    <BreadcrumbList>
      <BreadcrumbItem>
        <BreadcrumbLink href="/">
          Home
        </BreadcrumbLink>
      </BreadcrumbItem>
      <BreadcrumbSeparator>
        <SlashIcon />
      </BreadcrumbSeparator>
      <BreadcrumbItem>
        <BreadcrumbLink href="/components">
          Components
        </BreadcrumbLink>
      </BreadcrumbItem>
    </BreadcrumbList>
  </Breadcrumb>
</template>
```

----

### Dropdown
You can compose `<BreadcrumbItem />` with a `<DropdownMenu />` to create a dropdown in the breadcrumb.

::component-preview
---
name: BreadcrumbDropdownDemo
---
::

```vue showLineNumbers {2-7,13-22}
<script setup lang="ts">
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
//...
</script>

<template>
  <BreadcrumbItem>
    <DropdownMenu>
      <DropdownMenuTrigger>
        Components
      </DropdownMenuTrigger>
      <DropdownMenuContent align="start">
        <DropdownMenuItem>Documentation</DropdownMenuItem>
        <DropdownMenuItem>Themes</DropdownMenuItem>
        <DropdownMenuItem>GitHub</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  </BreadcrumbItem>
</template>
```

----

### Collapsed

We provide a `<BreadcrumbEllipsis />` component to show a collapsed state when the breadcrumb is too long.

::component-preview
---
name: BreadcrumbCollapsedDemo
---
::

```vue showLineNumbers {2,11}
<script setup lang="ts">
import { BreadcrumbEllipsis } from "@/components/ui/breadcrumb"
//...
</script>

<template>
  <Breadcrumb>
    <BreadcrumbList>
      
      <BreadcrumbItem>
        <BreadcrumbEllipsis />
      </BreadcrumbItem>
    
    </BreadcrumbList>
  </Breadcrumb>
</template>
```

----

### Link component
To use a custom link component from your routing library, you can use the `as-child` prop on `<BreadcrumbLink />`.

::component-preview
---
name: BreadcrumbLinkDemo
---
::

```vue showLineNumbers {2,10-12}
<script setup lang="ts">
import { NuxtLink } from '#components'
// ...
</script>

<template>
  <Breadcrumb>
    <BreadcrumbList>
      <BreadcrumbItem>
        <BreadcrumbLink as-child>
          <NuxtLink to="/">
            Home
          </NuxtLink>
        </BreadcrumbLink>
      </BreadcrumbItem>
      {/* ... */}
    </BreadcrumbList>
  </Breadcrumb>
</template>
```

----

### Responsive
Here's an example of a responsive breadcrumb that composes `<BreadcrumbItem />` with `<BreadcrumbEllipsis />`, ` <DropdownMenu />`, and `<Drawer />`.

It displays a dropdown on desktop and a drawer on mobile.

::component-preview
---
name: BreadcrumbResponsiveDemo
---
::

---

### Apps V4 Content Docs Components Bubble (`apps/v4/content/docs/components/bubble.md`)

---
title: Bubble
description: Displays conversational content in a message bubble. Supports variants, alignment, grouping, reactions, and collapsible content.
component: true
---

::component-preview
---
name: BubbleDemo
previewClass: h-auto theme-blue
---
::

The `Bubble` component displays framed conversational content. Use it for chat text, short structured output, quoted replies, suggestions, and reactions.

For full-featured chat interfaces, use the [`Message`](/docs/components/message) component. `Bubble` is intentionally scoped to the bubble surface. Place avatars, names, timestamps, metadata, and message-level actions in [`Message`](/docs/components/message).


## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add bubble
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/bubble) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Bubble, BubbleContent, BubbleReactions } from '@/components/ui/bubble'
</script>

<template>
  <Bubble>
    <BubbleContent>
      I checked the registry output and removed the stale route.
    </BubbleContent>
    <BubbleReactions>
      <span>👍</span>
    </BubbleReactions>
  </Bubble>
</template>
```

## Composition

Use the following composition to build a bubble:

```text
Bubble
├── BubbleContent
└── BubbleReactions
```

Use `BubbleGroup` to group consecutive bubbles from the same sender:

```text
BubbleGroup
├── Bubble
│   └── BubbleContent
└── Bubble
    └── BubbleContent
```

## Features

- Seven visual variants, from a strong primary bubble to unframed ghost content
- Start and end alignment for sender and receiver bubbles
- Reactions that anchor to the bubble edge with configurable side and alignment
- Bubbles size to their content, up to 80% of the container width
- Polymorphic content via `as` or `as-child` for link and button bubbles
- Customizable styling through the `class` prop on every part

## Examples

### Variants

Use `variant` to change the visual treatment of the bubble.

::component-preview
---
name: BubbleVariantsDemo
previewClass: h-auto theme-blue
---
::

| Variant       | Description                                            |
| ------------- | ------------------------------------------------------ |
| `default`     | A strong primary bubble, usually for the current user. |
| `secondary`   | The standard neutral bubble for conversation content.  |
| `muted`       | A lower-emphasis bubble for quiet supporting content.  |
| `tinted`      | A subtle primary-tinted bubble.                        |
| `outline`     | A bordered bubble for secondary or rich content.       |
| `ghost`       | Unframed content for assistant text or rich content.   |
| `destructive` | A destructive bubble for error or failed actions.      |

A bubble sizes to its content, up to 80% of the container width. The `ghost` variant removes the max-width so assistant text and rich content can span the full row.

### Alignment

Use `align` on `Bubble` to align the bubble to the start or end of the conversation.

::component-preview
---
name: BubbleAlignmentDemo
previewClass: h-auto theme-blue
---
::

| align   | Description                                        |
| ------- | -------------------------------------------------- |
| `start` | Align the bubble to the start of the conversation. |
| `end`   | Align the bubble to the end of the conversation.   |

**Note:** When building chat interfaces, you probably want to set `align` on the `Message` component itself. Bubbles inside `MessageContent` automatically follow the message alignment.

### Bubble Group

Use `BubbleGroup` to group consecutive bubbles from the same sender. Note the `align` prop should be set on the `Bubble` component itself, not the `BubbleGroup` component.

```text
BubbleGroup
├── Bubble
│   └── BubbleContent
└── Bubble
    └── BubbleContent
```

::component-preview
---
name: BubbleGroupDemo
previewClass: h-auto theme-blue
---
::

### Links and Buttons

Use `as-child` to merge `BubbleContent` styling and attributes onto a link or button passed through its default slot.

::component-preview
---
name: BubbleLinksAndButtonsDemo
previewClass: h-auto theme-blue
---
::

```vue showLineNumbers
<script setup lang="ts">
import { Bubble, BubbleContent } from '@/components/ui/bubble'
</script>

<template>
  <Bubble variant="muted">
    <BubbleContent as-child>
      <button type="button">Click here</button>
    </BubbleContent>
  </Bubble>
</template>
```

### Reactions

Use `BubbleReactions` for bubble reactions. You can use it to display reactions or quick action buttons. Use `side` and `align` to position the row — `side="top"` anchors it to the upper edge. Reactions overlap the bubble edge, so leave vertical space between rows — the examples below use a larger `gap` for this reason.

::component-preview
---
name: BubbleReactionsDemo
previewClass: h-auto theme-blue
---
::

### Show More / Collapsible

Long bubble content can be composed with [`Collapsible`](/docs/components/collapsible) to allow for a show more or show less interaction. Use the `CollapsibleTrigger` component to trigger the collapsible content.

::component-preview
---
name: BubbleCollapsibleDemo
previewClass: h-auto theme-blue
---
::

### Tooltip

Wrap a bubble in a [`Tooltip`](/docs/components/tooltip) to reveal metadata on hover, such as when a message was read.

::component-preview
---
name: BubbleTooltipDemo
previewClass: h-auto theme-blue
---
::

### Popover

Pair a bubble with a [`Popover`](/docs/components/popover) to surface more information on demand, such as the full error message for a failed action.

::component-preview
---
name: BubblePopoverDemo
previewClass: h-auto theme-blue
---
::

## Accessibility

`Bubble` renders the presentational message surface. Keep conversation-level semantics on the surrounding container and follow the guidelines below.

### Labeling Reactions

Reactions render as a row of emoji. A screen reader reads each glyph with no context, and counters like `+8` are announced as "plus eight". Group the row as a single image with a descriptive `aria-label` so it announces once. `role="img"` also hides the individual emoji from assistive tech, so no `aria-hidden` is needed.

```vue showLineNumbers
<BubbleReactions role="img" aria-label="Reactions: thumbs up, fire, and 8 more">
  <span>👍</span>
  <span>🔥</span>
  <span>+8</span>
</BubbleReactions>
```

When reactions are interactive, render buttons instead and give icon-only buttons an `aria-label`.

```vue showLineNumbers
<BubbleReactions>
  <Button aria-label="Thumbs up" variant="secondary" size="icon-xs">
    <ThumbsUpIcon />
  </Button>
</BubbleReactions>
```

### Interactive Bubbles

When a bubble is clickable, pass a real `<button>` or `<a>` through `BubbleContent` with `as-child` so it is focusable and exposes the correct role. `BubbleContent` ships a visible focus ring for interactive elements, and the accessible name comes from the bubble text. No extra label is needed.

```vue showLineNumbers
<Bubble variant="muted" align="end">
  <BubbleContent as-child>
    <button type="button" @click="onReply">
      I forgot my password
    </button>
  </BubbleContent>
</Bubble>
```

### Meaning Beyond Color

Bubble variants signal role and tone with color. Pair them with text, alignment, or icons so meaning is not conveyed by color alone. For a `destructive` bubble, keep the error context in the message text rather than relying on the color treatment.

## API Reference

All Bubble parts render a `<div>` by default. Use `as` to choose another element, or `as-child` to merge the component's attributes and styles onto the single element or component in its default slot.

### Bubble

The root bubble wrapper.

| Prop       | Type                                                                                       | Default     | Description                                                     |
| ---------- | ------------------------------------------------------------------------------------------ | ----------- | --------------------------------------------------------------- |
| `variant`  | `"default" \| "secondary" \| "muted" \| "tinted" \| "outline" \| "ghost" \| "destructive"` | `"default"` | The bubble visual treatment.                                    |
| `align`    | `"start" \| "end"`                                                                         | `"start"`   | The inline alignment of the bubble.                             |
| `as`       | `string \| Component`                                                                       | `"div"`     | The element or component to render.                             |
| `as-child` | `boolean`                                                                                    | `false`     | Render the default slot as the root and merge props onto it.    |
| `class`    | `string`                                                                                     | -           | Additional classes to apply to the root element.                |

### BubbleContent

The bubble content wrapper.

| Prop       | Type                 | Default | Description                                                  |
| ---------- | -------------------- | ------- | ------------------------------------------------------------ |
| `as`       | `string \| Component` | `"div"` | The element or component to render.                          |
| `as-child` | `boolean`            | `false` | Render the default slot as the root and merge props onto it. |
| `class`    | `string`             | -       | Additional classes to apply to the content element.          |

### BubbleReactions

Displays overlapped reactions for a bubble.

| Prop       | Type                 | Default    | Description                                                  |
| ---------- | -------------------- | ---------- | ------------------------------------------------------------ |
| `side`     | `"top" \| "bottom"` | `"bottom"` | The side of the bubble to anchor the reactions.              |
| `align`    | `"start" \| "end"`  | `"end"`    | The inline alignment of the reactions.                       |
| `as`       | `string \| Component` | `"div"`    | The element or component to render.                          |
| `as-child` | `boolean`            | `false`    | Render the default slot as the root and merge props onto it. |
| `class`    | `string`             | -          | Additional classes to apply to the reaction row.             |

### BubbleGroup

Groups consecutive bubbles from the same sender.

| Prop       | Type                 | Default | Description                                                  |
| ---------- | -------------------- | ------- | ------------------------------------------------------------ |
| `as`       | `string \| Component` | `"div"` | The element or component to render.                          |
| `as-child` | `boolean`            | `false` | Render the default slot as the root and merge props onto it. |
| `class`    | `string`             | -       | Additional classes to apply to the group root.               |

---

### Apps V4 Content Docs Components Button Group (`apps/v4/content/docs/components/button-group.md`)

---
title: Button Group
description: A container that groups related buttons together with consistent styling.
component: true
---

::component-preview
---
name: ButtonGroupDemo
class: '[&_.preview]:min-h-[400px]'
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add button-group
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/button-group) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue
<script setup lang="ts">
import {
  ButtonGroup,
  ButtonGroupSeparator,
  ButtonGroupText,
} from '@/components/ui/button-group'
</script>

<template>
  <ButtonGroup>
    <Button>Button 1</Button>
    <Button>Button 2</Button>
  </ButtonGroup>
</template>
```

## Accessibility

- The `ButtonGroup` component has the `role` attribute set to `group`.
- Use `Tab` to navigate between the buttons in the group.
- Use `aria-label` or `aria-labelledby` to label the button group.

```vue
<template>
  <ButtonGroup aria-label="Button group">
    <Button>Button 1</Button>
    <Button>Button 2</Button>
  </ButtonGroup>
</template>
```

## ButtonGroup vs ToggleGroup

- Use the `ButtonGroup` component when you want to group buttons that perform an action.
- Use the `ToggleGroup` component when you want to group buttons that toggle a state.

## Examples

### Orientation

Set the `orientation` prop to change the button group layout.

::component-preview
---
name: ButtonGroupOrientationDemo
---
::

### Size

Control the size of buttons using the `size` prop on individual buttons.

::component-preview
---
name: ButtonGroupSizeDemo
---
::

### Nested

`<ButtonGroup>` components to create button groups with spacing.

::component-preview
---
name: ButtonGroupNestedDemo
---
::

### Separator

The `ButtonGroupSeparator` component visually divides buttons within a group.

Buttons with variant `outline` do not need a separator since they have a border. For other variants, a separator is recommended to improve the visual hierarchy.

::component-preview
---
name: ButtonGroupSeparatorDemo
---
::

### Split

Create a split button group by adding two buttons separated by a `ButtonGroupSeparator`.

::component-preview
---
name: ButtonGroupSplitDemo
---
::

### Input

Wrap an `Input` component with buttons.

::component-preview
---
name: ButtonGroupWithInputDemo
---
::

### Input Group

Wrap an `InputGroup` component to create complex input layouts.

::component-preview
---
name: ButtonGroupInputGroupDemo
---
::

### Dropdown Menu

Create a split button group with a `DropdownMenu` component.

::component-preview
---
name: ButtonGroupWithDropdownMenuDemo
---
::

### Select

Pair with a `Select` component.

::component-preview
---
name: ButtonGroupWithSelectDemo
---
::

### Popover

Use with a `Popover` component.

::component-preview
---
name: ButtonGroupWithPopoverDemo
---
::

## API Reference

### ButtonGroup

The `ButtonGroup` component is a container that groups related buttons together with consistent styling.

| Prop         | Type                             | Default   |
| ------------ | -------------------------------- | --------- |
| `orientation` | `"horizontal"` \| `"vertical"`    | `"horizontal"` |

```vue
<template>
  <ButtonGroup>
    <Button>Button 1</Button>
    <Button>Button 2</Button>
  </ButtonGroup>
</template>
```

Nest multiple button groups to create complex layouts with spacing. See the [nested](#nested) example for more details.

```vue
<template>
  <ButtonGroup>
    <ButtonGroup />
    <ButtonGroup />
  </ButtonGroup>
</template>
```

### ButtonGroupSeparator

The `ButtonGroupSeparator` component visually divides buttons within a group.

| Prop      | Type   | Default |
| --------- | ------ | ------- |
| `orientation`    | `"horizontal" \| "vertical"` | `vertical`    |

```vue
<template>
  <ButtonGroup>
    <Button>Button 1</Button>
    <ButtonGroupSeparator />
    <Button>Button 2</Button>
  </ButtonGroup>
</template>
```

### ButtonGroupText

Use this component to display text within a button group.

| Prop      | Type   | Default |
| --------- | ------ | ------- |
| `as-child`    | boolean | `false`    |

```vue
<template>
  <ButtonGroup>
    <ButtonGroupText>Text</ButtonGroupText>
    <Button>Button</Button>
  </ButtonGroup>
</template>
```

Use the `as-child` prop to render a custom component as the text, for example a label.

```vue
<script setup lang="ts">
import { ButtonGroupText } from '@/components/ui/button-group'
import { Label } from '@/components/ui/label'
</script>

<template>
  <ButtonGroup>
    <ButtonGroupText as-child>
      <Label for="name">Text</Label>
    </ButtonGroupText>
    <Input id="name" placeholder="Type something here..." />
  </ButtonGroup>
</template>
```

---

### Apps V4 Content Docs Components Button (`apps/v4/content/docs/components/button.md`)

---
title: Button
description: Displays a button or a component that looks like a button.
featured: true
component: true
---

::component-preview
---
name: ButtonDemo
description: A button
---
::

::vue-school-link{class="mt-6" lesson="buttons-variants-and-the-as-child-prop-with-shadcn-vue" placement="top"}
Watch a Vue School video about buttons, variants, and the as-child prop in shadcn-vue.
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add button
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/button) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Button } from '@/components/ui/button'
</script>

<template>
  <Button variant="outline">
    Button
  </Button>
</template>
```

## Cursor

Tailwind v4 [switched](https://tailwindcss.com/docs/upgrade-guide#buttons-use-the-default-cursor) from `cursor: pointer` to `cursor: default` for the button component.

If you want to keep the `cursor: pointer` behavior, add the following code to your CSS file:

You can also enable this during project setup with `npx shadcn-vue@latest init --pointer`.

```css showLineNumbers title="tailwind.css"
@layer base {
  button:not(:disabled),
  [role="button"]:not(:disabled) {
    cursor: pointer;
  }
}
```

## Examples

### Size

::component-preview
---
name: ButtonSize
class: mb-4
---
::

### Default

::component-preview
---
name: ButtonDefault
class: mb-4
---
::

### Outline

::component-preview
---
name: ButtonOutline
class: mb-4
---
::

### Secondary

::component-preview
---
name: ButtonSecondary
class: mb-4
---
::

### Ghost

::component-preview
---
name: ButtonGhost
class: mb-4
---
::

### Destructive

::component-preview
---
name: ButtonDestructive
class: mb-4
---
::

### Link

::component-preview
---
name: ButtonLink
class: mb-4
---
::

### Icon

::component-preview
---
name: ButtonIcon
class: mb-4
---
::

```vue showLineNumbers
<template>
  <Button variant="outline" size="icon" aria-label="Submit">
    <CircleFadingArrowUpIcon />
  </Button>
</template>
```

### With Icon

The spacing between the icon and the text is automatically adjusted based on the size of the button. You do not need any margin on the icon.

::component-preview
---
name: ButtonWithIcon
class: mb-4
---
::

### Rounded

Use the `rounded-full` class to make the button rounded.

::component-preview
---
name: ButtonRounded
class: mb-4
---
::

### Spinner

::component-preview
---
name: ButtonLoading
class: mb-4
---
::

### Button Group

::component-preview
---
name: ButtonGroupDemo
class: mb-4
---
::

To create a button group, use the `ButtonGroup` component. See the [Button Group](/docs/components/button-group) documentation for more details.

### Link (asChild)

You can use the `as-child` prop to make another component look like a button. Here's an example of a link that looks like a button.

```vue showLineNumbers
<script setup lang="ts">
import { Button } from '@/components/ui/button'
</script>

<template>
  <Button as-child>
    <a href="/login">Login</a>
  </Button>
</template>
```

## API Reference

### Button

The `Button` component is a wrapper around the `button` element that adds a variety of styles and functionality.

| Prop      | Type                                                                          | Default     |
| --------- | ----------------------------------------------------------------------------- | ----------- |
| `variant` | `"default" \| "outline" \| "ghost" \| "destructive" \| "secondary" \| "link"` | `"default"` |
| `size`    | `"default" \| "sm" \| "lg" \| "icon" \| "icon-sm" \| "icon-lg"`               | `"default"` |
| `asChild` | `boolean`                                                                     | `false`     |

---

### Apps V4 Content Docs Components Calendar (`apps/v4/content/docs/components/calendar.md`)

---
title: Calendar
description: A date field component that allows users to enter and edit date.
component: true
links:
  doc: https://reka-ui.com/docs/components/calendar
  api: https://reka-ui.com/docs/components/calendar#api-reference
---

::component-preview
---
name: CalendarDemo
description: A calendar component.
previewClass: h-[26rem]
---
::

## About

The `<Calendar />` component is built on top of the [Reka UI Calendar](https://www.reka-ui.com/docs/components/calendar) component, which uses the [@internationalized/date](https://react-spectrum.adobe.com/internationalized/date/index.html) package to handle dates.

If you're looking for a range calendar, check out the [Range Calendar](./range-calendar.md) component.

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add calendar
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/calendar) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Calendar } from '@/components/ui/calendar'
</script>

<template>
  <Calendar />
</template>
```

## Calendar Systems (Persian / Hijri / Jalali for example)

[@internationalized/date](https://react-spectrum.adobe.com/internationalized/date/index.html) Supports 13 calendar systems
Here, we'll use the Persian calendar as an example to show how to use calendar systems with the `<Calendar />` component or any other Calendar components.

The default calendar system is `gregory`.<br/>
To use a different calendar system, you need to provide a value with the desired system through the **`defaultPlaceholder`** or **`placeholder`** props.

It's recommended to add either the `placeholder` or `defaultPlaceholder` to the component even if you don't use any other calendar system

```vue
<script setup lang="ts">
import type { DateValue } from '@internationalized/date'
import { getLocalTimeZone, PersianCalendar, toCalendar, today } from '@internationalized/date'
import { Calendar } from '@/registry/new-york-v4/ui/calendar'

const date = ref(today(getLocalTimeZone())) as Ref<DateValue> // no need to add calendar identifier to modelValue when using placeholder

const placeholder = ref(toCalendar(today(getLocalTimeZone()), new PersianCalendar())) as Ref<DateValue>
// or
const defaultPlaceholder = toCalendar(today(getLocalTimeZone()))
</script>

<template>
  <Calendar
    v-model="date"
    v-model:placeholder="placeholder"
    locale="fa-IR"
  />
  
  <Calendar
    v-model="date"
    :default-placeholder="placeholder"
    locale="fa-IR"
  />
</template>
```

If none of these props are provided, the emitted dates will use the `gregorian` calendar by default, since it is the most widely used system.

The emitted value from the Calendar component will vary depending on the specified calendar system identifier. <br />

You can also change the locale using the `locale` prop to match the calendar system interface.

::code-collapsible-wrapper

```vue showLineNumbers
<script setup lang="ts">
import {
  CalendarDate,
  fromDate,
  getLocalTimeZone,
  parseDate,
  PersianCalendar,
  toCalendar,
  today
} from '@internationalized/date'
import { ref } from 'vue'

const date = ref(toCalendar(new CalendarDate(2025, 1, 1), new PersianCalendar()))
// or
const date = ref(toCalendar(parseDate('2022-02-03'), new PersianCalendar()))
// or
const date = ref(toCalendar(today(getLocalTimeZone()), new PersianCalendar()))
// or
const date = ref(new CalendarDate(new PersianCalendar(), 1404, 1, 1))
// or
const date = ref(toCalendar(fromDate(new Date(), getLocalTimeZone()), new PersianCalendar()))

const placeholder = ref(toCalendar(today(getLocalTimeZone()), new PersianCalendar()))
</script>

<template>
  <Calendar
    v-model="date"
    v-model:placeholder="placeholder"
    locale="fa-IR"
    dir="rtl"
  />
</template>
```

::

::component-preview
---
name: CalendarPersianDemo
description: A Persian calendar.
previewClass: h-[30rem]
---
::

## Examples

### Calendar Systems

importing `createCalendar` into your project will result in all available calendars being included in your bundle. If you wish to limit the supported calendars to reduce bundle sizes, you can create your own implementation that only imports the desired classes. This way, your bundler can tree-shake the unused calendar implementations.

Check [`@internationalized/date`](https://react-spectrum.adobe.com/internationalized/date/), especially the section on [**Calendar Identifiers**](https://react-spectrum.adobe.com/internationalized/date/Calendar.html#calendar-identifiers).

```ts
import { GregorianCalendar, JapaneseCalendar } from '@internationalized/date'

function createCalendar(identifier) {
  switch (identifier) {
    case 'gregory':
      return new GregorianCalendar()
    case 'japanese':
      return new JapaneseCalendar()
    default:
      throw new Error(`Unsupported calendar ${identifier}`)
  }
}
```

::component-preview
---
name: CalendarSystems
title: Calendar systems
description: A calendar with multiple calendar systems
class: 'mt-8'
previewClass: h-[600px]
---
::

### Month and Year Selector

Make sure to pass either the `placeholder` or `defaultPlaceholder` prop when using this feature.

::component-preview
---
name: CalendarYearAndMonthSelector
title: Month and Year Selector
description: A calendar with month and year dropdowns.
previewClass: h-[32rem]
---
::

### Date of Birth Picker

::component-preview
---
name: CalendarDateBirth
title: Date of Birth Picker
description: A calendar with date of birth picker.
---
::

### Date and Time Picker

::component-preview
---
name: CalendarDateAndTimePicker
title: Date and Time Picker
description: A calendar with date and time picker.
---
::

### Natural Language Picker

This component uses the `chrono-node` library to parse natural language dates.

::component-preview
---
name: CalendarNaturalLanguagePicker
title: Natural Language Picker
description: A calendar with natural language picker.
---
::

### Custom Heading and Cell Size

::component-preview
---
name: CalendarCustomCellSize
title: Custom Heading and Cell Size
description: A calendar with custom cell size that's responsive.
class: '**:[.preview]:h-[560px]'
---
::

---

### Apps V4 Content Docs Components Card (`apps/v4/content/docs/components/card.md`)

---
title: Card
description: Displays a card with header, content, and footer.
component: true
---

::component-preview
---
name: CardDemo
description: A card with a form
previewClass: h-[32rem] sm:h-[30rem]
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add card
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/card) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from '@/components/ui/card'
</script>

<template>
  <Card>
    <CardHeader>
      <CardTitle>Card Title</CardTitle>
      <CardDescription>Card Description</CardDescription>
    </CardHeader>
    <CardContent>
      <p>Card Content</p>
    </CardContent>
    <CardFooter>
      <p>Card Footer</p>
    </CardFooter>
  </Card>
</template>
```

---

### Apps V4 Content Docs Components Carousel (`apps/v4/content/docs/components/carousel.md`)

---
title: Carousel
description: A carousel with motion and swipe built using Embla.
component: true
---

::component-preview
---
name: CarouselDemo
title: Carousel
description: A carousel component.
previewClass: h-[25rem] sm:h-[32rem]
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add carousel
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install embla-carousel-vue
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/carousel) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Carousel,
  CarouselContent,
  CarouselItem,
  CarouselNext,
  CarouselPrevious,
} from '@/components/ui/carousel'
</script>

<template>
  <Carousel>
    <CarouselContent>
      <CarouselItem>...</CarouselItem>
      <CarouselItem>...</CarouselItem>
      <CarouselItem>...</CarouselItem>
    </CarouselContent>
    <CarouselPrevious />
    <CarouselNext />
  </Carousel>
</template>
```

## Examples

### Sizes

To set the size of the items, you can use the `basis` utility class on the `<CarouselItem />`.

::component-preview
---
name: CarouselSize
title: Carousel
description: A carousel with 3 active items of equal size.
previewClass: h-[25rem] sm:h-72
---
::

```vue showLineNumbers {5-7}
// 33% of the carousel width.
<template>
  <Carousel>
    <CarouselContent>
      <CarouselItem class="basis-1/3">
        ...
      </CarouselItem>
      <CarouselItem class="basis-1/3">
        ...
      </CarouselItem>
      <CarouselItem class="basis-1/3">
        ...
      </CarouselItem>
    </CarouselContent>
  </Carousel>
</template>
```

```vue showLineNumbers {5-7}
// 50% on small screens and 33% on larger screens.
<template>
  <Carousel>
    <CarouselContent>
      <CarouselItem class="md:basis-1/2 lg:basis-1/3">
        ...
      </CarouselItem>
      <CarouselItem class="md:basis-1/2 lg:basis-1/3">
        ...
      </CarouselItem>
      <CarouselItem class="md:basis-1/2 lg:basis-1/3">
        ...
      </CarouselItem>
    </CarouselContent>
  </Carousel>
</template>
```

### Spacing

To set the spacing between the items, we use a `pl-[VALUE]` utility on the `<CarouselItem />` and a negative `-ml-[VALUE]` on the `<CarouselContent />`.

::callout{class="mt-6"}
  **Why:** I tried to use the `gap` property or a `grid` layout on the `
  <CarouselContent />` but it required a lot of math and mental effort to get the
  spacing right. I found `pl-[VALUE]` and `-ml-[VALUE]` utilities much easier to
  use.

You can always adjust this in your own project if you need to.

::

::component-preview
---
name: CarouselSpacing
title: Carousel
description: A carousel with 3 items with a spacing of 1rem.
previewClass: h-[25rem] sm:h-72
---
::

```vue showLineNumbers /-ml-4/ /pl-4/
<template>
  <Carousel>
    <CarouselContent class="-ml-4">
      <CarouselItem class="pl-4">
        ...
      </CarouselItem>
      <CarouselItem class="pl-4">
        ...
      </CarouselItem>
      <CarouselItem class="pl-4">
        ...
      </CarouselItem>
    </CarouselContent>
  </Carousel>
</template>
```

```vue showLineNumbers /-ml-2/ /pl-2/ /md:-ml-4/ /md:pl-4/
<template>
  <Carousel>
    <CarouselContent class="-ml-2 md:-ml-4">
      <CarouselItem class="pl-2 md:pl-4">
        ...
      </CarouselItem>
      <CarouselItem class="pl-2 md:pl-4">
        ...
      </CarouselItem>
      <CarouselItem class="pl-2 md:pl-4">
        ...
      </CarouselItem>
    </CarouselContent>
  </Carousel>
</template>
```

### Orientation

Use the `orientation` prop to set the orientation of the carousel.

::component-preview
---
name: CarouselOrientation
title: Carousel
description: A vertical carousel.
previewClass: h-[32rem]
---
::

```vue
<Carousel orientation="vertical | horizontal">
  ...
</Carousel>
```

## Options

You can pass options to the carousel using the `opts` prop. See the [Embla Carousel docs](https://www.embla-carousel.com/api/options/) for more information.

```vue showLineNumbers {3-6}
<template>
  <Carousel
    :opts="{
      align: 'start',
      loop: true,
    }"
  >
    <CarouselContent>
      <CarouselItem>...</CarouselItem>
      <CarouselItem>...</CarouselItem>
      <CarouselItem>...</CarouselItem>
    </CarouselContent>
  </Carousel>
</template>
```

## API

### Method 1

Use the `@init-api` emit method on `<Carousel />` component to set the instance of the API.

::component-preview
---
name: CarouselApi
title: Carousel
description: A carousel with a slide counter.
previewClass: h-[27rem] sm:h-[32rem]
---
::

### Method 2

You can access it through setting a template ref on the `<Carousel />` component.

```vue showLineNumbers {2,5,10}
<script setup lang="ts">
const carouselContainerRef = ref<InstanceType<typeof Carousel> | null>(null)

function accessApi() {
  carouselContainerRef.value?.carouselApi.on('select', () => {})
}
</script>

<template>
  <Carousel ref="carouselContainerRef">
    ...
  </Carousel>
</template>
```

## Events

You can listen to events using the API. To get the API instance use the `@init-api` emit method on the `<Carousel />` component

```vue showLineNumbers {5,7-9,25}
<script setup lang="ts">
import { nextTick, ref, watch } from 'vue'
import { useCarousel } from '@/components/ui/carousel'

const api = ref<CarouselApi>()

function setApi(val: CarouselApi) {
  api.value = val
}

const stop = watch(api, (api) => {
  if (!api)
    return

  // Watch only once or use watchOnce() in @vueuse/core
  nextTick(() => stop())

  api.on('select', () => {
    // Do something on select.
  })
})
</script>

<template>
  <Carousel @init-api="setApi">
    ...
  </Carousel>
</template>
```

See the [Embla Carousel docs](https://www.embla-carousel.com/api/events/) for more information on using events.

## Slot Props

You can get the reactive slot props like `carouselRef, canScrollNext..Prev, scrollNext..Prev` using the `v-slot` directive in the `<Carousel v-slot="slotProps" />` component to extend the functionality.

```vue showLineNumbers {2}
<template>
  <Carousel v-slot="{ canScrollNext, canScrollPrev }">
    ...
    <CarouselPrevious v-if="canScrollPrev" />
    <CarouselNext v-if="canScrollNext" />
  </Carousel>
</template>
```

## Plugins

You can use the `plugins` prop to add plugins to the carousel.

```bash
npm install embla-carousel-autoplay
```

```vue showLineNumbers {2,8-10}
<script setup lang="ts">
import Autoplay from 'embla-carousel-autoplay'
</script>

<template>
  <Carousel
    class="w-full max-w-xs"
    :plugins="[Autoplay({
      delay: 2000,
    })]"
  >
    ...
  </Carousel>
</template>
```

::component-preview
---
name: CarouselPlugin
title: Carousel
description: A carousel with the autoplay plugin.
previewClass: h-[25rem] sm:h-[32rem]
---
::

See the [Embla Carousel docs](https://www.embla-carousel.com/api/plugins/) for more information on using plugins.

---

### Apps V4 Content Docs Components Chart (`apps/v4/content/docs/components/chart.md`)

---
title: Chart
description: Beautiful charts. Built using Unovis. Copy and paste into your apps.
component: true
---

::component-preview
---
name: "ChartBarInteractive"
class: "theme-blue [&_.preview]:h-auto [&_.preview]:p-0 [&_.preview]:lg:min-h-[404px] [&_.preview>div]:w-full [&_.preview>div]:border-none [&_.preview>div]:shadow-none"
hideCode: true
---
::

Introducing **Charts**. A collection of chart components that you can copy and paste into your apps.

Charts are designed to look great out of the box. They work well with the other components and are fully customizable to fit your project.

[Browse the Charts Library](/charts).

## Component

We use [Unovis](https://unovis.dev/) under the hood.

We designed the `chart` component with composition in mind. **You build your charts using Unovis components and only bring in custom components, such as `ChartTooltip`, when and where you need it**.

```vue showLineNumbers
<script setup lang="ts">
import { VisGroupedBar, VisXYContainer } from '@unovis/vue'
import { ChartContainer, ChartTooltipContent } from '@/components/ui/chart'
</script>

<template>
  <ChartContainer :config="chartConfig">
    <VisXYContainer :data="data">
      <VisGroupedBar :x="(d) => d.month" :y="(d) => d.value" />
      <ChartTooltip :template="componentToString(chartConfig, ChartTooltipContent)" />
    </VisXYContainer>
  </ChartContainer>
</template>
```

We do not wrap Unovis. This means you're not locked into an abstraction. When a new Unovis version is released, you can follow the official upgrade path to upgrade your charts.

**The components are yours**.

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add chart
```

::

::::tabs-content{value="manual"}

  :::steps
    ::step
    Install the following dependencies
    ::

    ```bash
    npm install @unovis/ts @unovis/vue
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/chart) into your project
    ::

    ::step
    Update the import paths to match your project setup
    ::

    ::step
    Add the following colors to your CSS file
    ::

    ```css
    @layer base {
      :root {
        --chart-1: oklch(0.646 0.222 41.116);
        --chart-2: oklch(0.6 0.118 184.704);
        --chart-3: oklch(0.398 0.07 227.392);
        --chart-4: oklch(0.828 0.189 84.429);
        --chart-5: oklch(0.769 0.188 70.08);
      }

      .dark {
        --chart-1: oklch(0.488 0.243 264.376);
        --chart-2: oklch(0.696 0.17 162.48);
        --chart-3: oklch(0.769 0.188 70.08);
        --chart-4: oklch(0.627 0.265 303.9);
        --chart-5: oklch(0.645 0.246 16.439);
      }
    }
    ```
  :::

::::

:::::

## Usage

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Your First Chart

Let's build your first chart. We'll build a bar chart, add a grid, axis, tooltip and legend.

:::steps

  ::step
  Start by defining your data
  ::

  The following data represents the number of desktop and mobile users for each month.

  ```ts showLineNumbers
  const chartData = [
    { month: 'January', desktop: 186, mobile: 80 },
    { month: 'February', desktop: 305, mobile: 200 },
    { month: 'March', desktop: 237, mobile: 120 },
    { month: 'April', desktop: 73, mobile: 190 },
    { month: 'May', desktop: 209, mobile: 130 },
    { month: 'June', desktop: 214, mobile: 140 },
  ]
  ```

  ::step
  Define your chart config
  ::

  The chart config holds configuration for the chart. This is where you place human-readable strings, such as labels, icons and color tokens for theming.

  ```ts showLineNumbers
  import type { ChartConfig } from '@/components/ui/chart'

  const chartConfig = {
    desktop: {
      label: 'Desktop',
      color: 'var(--chart-1)',
    },
    mobile: {
      label: 'Mobile',
      color: 'var(--chart-2)',
    },
  } satisfies ChartConfig
  ```

  ::step
  Build your chart
  ::

  You can now build your chart using Unovis components.

  ::component-source{name="ChartBarDemo" title="components/ExampleChart.vue"}
  ::

  ::component-preview
  ---
  name: ChartBarDemo
  class: '[&_.preview]:min-h-[250px] [&_.preview]:p-4'
  ---
  ::

:::

### Add an Axis

To add axes to the chart, we use the `VisAxis` component.

:::steps

  ::step
  Import the `VisAxis` component
  ::

  ```vue showLineNumbers
  import { VisAxis, VisGroupedBar, VisXYContainer } from '@unovis/vue'
  ```

  ::step
  Add the `VisAxis` components to your chart
  ::

  ```vue showLineNumbers
  <template>
    <VisAxis
      type="x"
      :x="(d: Data) => d.date"
      :tick-line="false"
      :domain-line="false"
      :grid-line="false"
      :tick-format="(d: number) => {
        const date = new Date(d)
        return date.toLocaleDateString('en-US', {
          month: 'short',
        })
      }"
      :tick-values="chartData.map(d => d.date)"
    />
    <VisAxis
      type="y"
      :tick-format="(d: number) => ''"
      :tick-line="false"
      :domain-line="false"
      :grid-line="true"
    />
  </template>
  ```

  ::component-preview
  ---
  name: ChartBarDemoAxis
  class: '[&_.preview]:min-h-[250px] [&_.preview]:p-4'
  ---
  ::

:::

### Add Tooltip

To add a tooltip, we'll use the custom `ChartTooltip` and `ChartTooltipContent` components from `chart`.

:::steps

  ::step
  Import the `ChartTooltip` and `ChartTooltipContent` components
  ::

  ```ts
  import { ChartTooltip, ChartTooltipContent, componentToString } from '@/components/ui/chart'
  ```

  ::step
  Add the components to your chart
  ::

  ```vue showLineNumbers
  <ChartTooltip />

  <ChartCrosshair :template="componentToString(chartConfig, ChartTooltipContent)" />
  ```

  ::component-preview
  ---
  name: ChartBarDemoTooltip
  class: '[&_.preview]:min-h-[250px] [&_.preview]:p-4'
  ---
  ::

  Hover to see the tooltips. Easy, right? Two components, and we've got a beautiful tooltip.

:::

### Add Legend

We'll do the same for the legend. We'll use the `ChartLegend` and `ChartLegendContent` components from `chart`.

:::steps

  ::step
  Import the `ChartLegendContent` components.
  ::

  ```ts
  import { ChartLegendContent } from '@/components/ui/chart'
  ```

  ::step
  Add the components to your chart.
  ::

  ```vue showLineNumbers {4}
  <template>
    <ChartContainer :config="chartConfig" class="min-h-[200px] w-full">
      <VisXYContainer :data="chartData" />
      <ChartLegendContent />
    </ChartContainer>
  </template>
  ```

  ::component-preview
  ---
  name: ChartBarDemoLegend
  class: '[&_.preview]:min-h-[250px] [&_.preview]:p-4'
  ---
  ::
:::

Done. You've built your first chart! What's next?

- [Themes and Colors](/docs/components/chart#theming)
- [Tooltip](/docs/components/chart#tooltip)
- [Legend](/docs/components/chart#legend)

## Chart Config

The chart config is where you define the labels, icons and colors for a chart.

It is intentionally decoupled from chart data.

This allows you to share config and color tokens between charts. It can also works independently for cases where your data or color tokens live remotely or in a different format.

```vue showLineNumbers
<script setup lang="ts">
import type { ChartConfig } from '@/components/ui/chart'
import { Monitor } from '@lucide/vue'

const chartConfig = {
  desktop: {
    label: 'Desktop',
    icon: Monitor,
    // A color like 'hsl(220, 98%, 61%)' or 'var(--color-name)'
    color: 'var(--chart-1)',
    // OR a theme object with 'light' and 'dark' keys
    theme: {
      light: 'var(--chart-1)',
      dark: 'var(--chart-2)',
    },
  },
} satisfies ChartConfig
</script>
```

## Theming

Charts has built-in support for theming. You can use css variables (recommended) or color values in any color format, such as hex, hsl or oklch.

### CSS Variables

:::steps

  ::step
  Define your colors in your css file
  ::

  ```css showLineNumbers
  @layer base {
    :root {
      --chart-1: oklch(0.646 0.222 41.116);
      --chart-2: oklch(0.6 0.118 184.704);
    }

    .dark {
      --chart-1: oklch(0.488 0.243 264.376);
      --chart-2: oklch(0.696 0.17 162.48);
    }
  }
  ```

  ::step
  Add the color to your `chartConfig`
  ::

  ```ts showLineNumbers {4,8}
  const chartConfig = {
    desktop: {
      label: 'Desktop',
      color: 'var(--chart-1)',
    },
    mobile: {
      label: 'Mobile',
      color: 'var(--chart-2)',
    },
  } satisfies ChartConfig
  ```

:::

### hex, hsl or oklch

You can also define your colors directly in the chart config. Use the color format you prefer.

```ts showLineNumbers
const chartConfig = {
  desktop: {
    label: 'Desktop',
    color: '#2563eb',
  },
} satisfies ChartConfig
```

### Using Colors

To use the theme colors in your chart, reference the colors using the format `var(--color-KEY)`.

#### Components

```vue showLineNumbers
<VisGroupedBar
  :x="(d) => d.month"
  :y="(d) => d.desktop"
  color="var(--color-desktop)"
/>
```

#### Chart Data

```ts showLineNumbers
const chartData = [
  { browser: 'chrome', visitors: 275, fill: 'var(--color-chrome)' },
  { browser: 'safari', visitors: 200, fill: 'var(--color-safari)' },
]
```

## Tooltip

A chart tooltip contains a label, name, indicator and value. You can use a combination of these to customize your tooltip.

You can turn on/off any of these using the `hideLabel`, `hideIndicator` props and customize the indicator style using the `indicator` prop.

Use `labelKey` and `nameKey` to use a custom key for the tooltip label and name.

Chart comes with the `ChartTooltip` and `ChartTooltipContent` components. You can use these two components to add custom tooltips to your chart.

```ts showLineNumbers
import { ChartTooltip, ChartTooltipContent } from '@/components/ui/chart'
```

```vue showLineNumbers
<template>
  <ChartTooltip />
  <ChartCrosshair
    :template="componentToString(chartConfig, ChartTooltipContent)"
  />
</template>
```

### Props

Use the following props to customize the tooltip.

| Prop            | Type                     | Description                                  |
| :-------------- | :----------------------- | :------------------------------------------- |
| `labelKey`      | string                   | The config or data key to use for the label. |
| `nameKey`       | string                   | The config or data key to use for the name.  |
| `indicator`     | `dot` `line` or `dashed` | The indicator style for the tooltip.         |
| `hideLabel`     | boolean                  | Whether to hide the label.                   |
| `hideIndicator` | boolean                  | Whether to hide the indicator.               |

### Colors

Colors are automatically referenced from the chart config.

### Custom

To use a custom key for tooltip label and names, use the `labelKey` and `nameKey` props.

```ts showLineNumbers
const chartData = [
  { browser: 'chrome', visitors: 187, fill: 'var(--color-chrome)' },
  { browser: 'safari', visitors: 200, fill: 'var(--color-safari)' },
]

const chartConfig = {
  visitors: {
    label: 'Total Visitors',
  },
  chrome: {
    label: 'Chrome',
    color: 'var(--chart-1)',
  },
  safari: {
    label: 'Safari',
    color: 'var(--chart-2)',
  },
} satisfies ChartConfig
```

```vue showLineNumbers
<template>
  <ChartCrosshair
    :template="componentToString(chartConfig, ChartTooltipContent, {
      labelKey: 'visitors',
      nameKey: 'browser',
    })"
  />
</template>
```

This will use `Total Visitors` for label and `Chrome` and `Safari` for the tooltip names.

## Legend

You can use the custom `<ChartLegendContent>` components to add a legend to your chart.

```ts
import { ChartLegendContent } from '@/components/ui/chart'
```

```vue
<template>
  <ChartLegendContent />
</template>
```

### Colors

Colors are automatically referenced from the chart config.

### Custom

To use a custom key for legend names, use the `nameKey` prop.

```tsx showLineNumbers /browser/
const chartData = [
  { browser: 'chrome', visitors: 187, fill: 'var(--color-chrome)' },
  { browser: 'safari', visitors: 200, fill: 'var(--color-safari)' },
]

const chartConfig = {
  chrome: {
    label: 'Chrome',
    color: 'hsl(var(--chart-1))',
  },
  safari: {
    label: 'Safari',
    color: 'hsl(var(--chart-2))',
  },
} satisfies ChartConfig
```

```vue
<template>
  <ChartLegendContent name-key="browser" />
</template>
```

This will use `Chrome` and `Safari` for the legend names.

---

### Apps V4 Content Docs Components Checkbox (`apps/v4/content/docs/components/checkbox.md`)

---
title: Checkbox
description: A control that allows the user to toggle between checked and not checked.
component: true
links:
  doc: https://reka-ui.com/docs/components/checkbox
  api: https://reka-ui.com/docs/components/checkbox#api-reference
---

::component-preview
---
name: CheckboxDemo
description: A checkbox
previewClass: h-88 sm:h-80
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add checkbox
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/checkbox) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Checkbox } from '@/components/ui/checkbox'
</script>

<template>
  <Checkbox />
</template>
```

## Checked State

Use `default-value` for uncontrolled checkboxes, or `v-model` to control the state.

```vue showLineNumbers
<script setup lang="ts">
import { ref } from 'vue'
import { Checkbox } from '@/components/ui/checkbox'

const checked = ref<boolean | 'indeterminate'>(false)
</script>

<template>
  <Checkbox v-model="checked" />
</template>
```

---

### Apps V4 Content Docs Components Collapsible (`apps/v4/content/docs/components/collapsible.md`)

---
title: Collapsible
description: An interactive component which expands/collapses a panel.
component: true
links:
  doc: https://reka-ui.com/docs/components/collapsible
  api: https://reka-ui.com/docs/components/collapsible#api-reference
---

::component-preview
---
name: CollapsibleDemo
description: A collapsible component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add collapsible
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/collapsible) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from '@/components/ui/collapsible'
</script>

<template>
  <Collapsible>
    <CollapsibleTrigger>Can I use this in my project?</CollapsibleTrigger>
    <CollapsibleContent>
      Yes. Free to use for personal and commercial projects. No attribution
      required.
    </CollapsibleContent>
  </Collapsible>
</template>
```

---

### Apps V4 Content Docs Components Combobox (`apps/v4/content/docs/components/combobox.md`)

---
title: Combobox
description: Autocomplete input and command palette with a list of suggestions.
component: true
links:
  doc: https://reka-ui.com/docs/components/combobox
  api: https://reka-ui.com/docs/components/combobox#api-reference
---

::component-preview
---
name: ComboboxDemo
description: A combobox with a list of frameworks.
---
::

A combobox can be built in two ways:

- Using the `Combobox` components, built on top of the [Reka UI Combobox](https://reka-ui.com/docs/components/combobox).
- Composing the `Popover` and the `Command` components as a listbox.

## Installation

### Combobox

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add combobox
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui @vueuse/core
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/combobox) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::


### Popover and Command

The listbox variant is built using a composition of the `Popover` and the `Command` components.

See installation instructions for the [Popover](/docs/components/popover#installation) and the [Command](/docs/components/command#installation) components.

## Usage

### Combobox

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Popover and Command

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Examples

### User

A combobox with custom item rendering and an action item.

::component-preview
---
name: ComboboxUserDemo
description: A combobox with a list of users.
---
::

### Timezone

A combobox with grouped items and a scrollable viewport.

::component-preview
---
name: ComboboxTimezoneDemo
description: A combobox with a list of timezones.
---
::

### Multiple

A combobox with multiple selection.

::component-preview
---
name: ComboboxMultipleDemo
description: A combobox with multiple selection.
---
::

### With Listbox

A combobox built with the `Popover` and the `Command` components.

::component-preview
---
name: ComboboxWithListboxDemo
description: A combobox built with the Popover and Command components.
---
::

---

### Apps V4 Content Docs Components Command (`apps/v4/content/docs/components/command.md`)

---
title: Command
description: Fast, composable, unstyled command menu for Vue.
component: true
links:
  doc: https://reka-ui.com/docs/components/listbox
  api: https://reka-ui.com/docs/components/listbox#api-reference
---

::component-preview
---
name: CommandDemo
description: A command component.
class: "[&_.preview>div]:max-w-[450px]"
previewClass: h-[24.5rem]
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add command
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/command) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Command,
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
  CommandShortcut,
} from '@/components/ui/command'
</script>

<template>
  <Command>
    <CommandInput placeholder="Type a command or search..." />
    <CommandList>
      <CommandEmpty>No results found.</CommandEmpty>
      <CommandGroup heading="Suggestions">
        <CommandItem>Calendar</CommandItem>
        <CommandItem>Search Emoji</CommandItem>
        <CommandItem>Calculator</CommandItem>
      </CommandGroup>
      <CommandSeparator />
      <CommandGroup heading="Settings">
        <CommandItem>Profile</CommandItem>
        <CommandItem>Billing</CommandItem>
        <CommandItem>Settings</CommandItem>
      </CommandGroup>
    </CommandList>
  </Command>
</template>
```

---

### Apps V4 Content Docs Components Context Menu (`apps/v4/content/docs/components/context-menu.md`)

---
title: Context Menu
description: Displays a menu to the user — such as a set of actions or functions — triggered by a right click.
component: true
links:
  doc: https://reka-ui.com/docs/components/context-menu
  api: https://reka-ui.com/docs/components/context-menu#api-reference
---

::component-preview
---
name: ContextMenuDemo
description: A context menu component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add context-menu
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/context-menu) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuTrigger,
} from '@/components/ui/context-menu'
</script>

<template>
  <ContextMenu>
    <ContextMenuTrigger>Right click</ContextMenuTrigger>
    <ContextMenuContent>
      <ContextMenuItem>Profile</ContextMenuItem>
      <ContextMenuItem>Billing</ContextMenuItem>
      <ContextMenuItem>Team</ContextMenuItem>
      <ContextMenuSeparator />
      <ContextMenuItem>Subscription</ContextMenuItem>
    </ContextMenuContent>
  </ContextMenu>
</template>
```

---

### Apps V4 Content Docs Components Data Table (`apps/v4/content/docs/components/data-table.md`)

---
title: Data Table
description: Powerful table and datagrids built using TanStack Table.
component: true
---

::component-preview
---
name: DataTableDemo
description: A data table with sorting, filtering, and pagination.
align: start
previewClass: items-start h-auto px-4 md:px-8
---
::

::vue-school-link{class="mt-6" lesson="data-tables-and-sonner-in-shadcn-vue" placement="top"}
Watch a Vue School video about data tables in shadcn-vue.
::

## Introduction

Every data table or datagrid I've created has been unique. They all behave differently, have specific sorting and filtering requirements, and work with different data sources.

It doesn't make sense to combine all of these variations into a single component. If we do that, we'll lose the flexibility that [headless UI](https://tanstack.com/table/latest/docs/introduction#what-is-headless-ui) provides.

So instead of a data-table component, I thought it would be more helpful to provide a guide on how to build your own.

We'll start with the basic `<Table />` component and build a complex data table from scratch.

::callout{class="mt-4"}

**Tip:** If you find yourself using the same table in multiple places in your app, you can always extract it into a reusable component.

::

## Table of Contents

This guide will show you how to use [TanStack Table](https://tanstack.com/table) and the `<Table />` component to build your own custom data table. We'll cover the following topics:

- [Set up Table Features](#set-up-table-features)
- [Basic Table](#basic-table)
- [Row Actions](#row-actions)
- [Pagination](#pagination)
- [Sorting](#sorting)
- [Filtering](#filtering)
- [Visibility](#visibility)
- [Row Selection](#row-selection)
- [Expanding](#expanding)
- [Reusable Components](#reusable-components)


## Installation

1. Add the `<Table />` component to your project:

```bash
npx shadcn-vue@latest add table
```

2. Add the `@tanstack/vue-table` dependency. This guide uses **TanStack Table v9**:

```bash
npm install @tanstack/vue-table
```



## Prerequisites

We are going to build a table to show recent payments. Here's what our data looks like:

```ts showLineNumbers
interface Payment {
  id: string
  amount: number
  status: 'pending' | 'processing' | 'success' | 'failed'
  email: string
}

export const payments: Payment[] = [
  {
    id: '728ed52f',
    amount: 100,
    status: 'pending',
    email: 'm@example.com',
  },
  {
    id: '489e1d42',
    amount: 125,
    status: 'processing',
    email: 'example@gmail.com',
  },
  // ...
]
```

## Project Structure

Start by creating the following file structure:

```ansi
 components
    └── payments
          ├── columns.ts
          ├── features.ts
          ├── data-table.vue
          ├── data-table-dropdown.vue
└── app.vue
```

I'm using a Nuxt example here but this works for any other Vue framework.

- `columns.ts` It will contain our column definitions.
- `features.ts` It will contain the shared `features` object that tells TanStack Table which behavior to enable.
- `data-table.vue` It will contain our `<DataTable />` component.
- `data-table-dropdown.vue` It will contain our `<DropdownAction />` component.
- `app.vue` This is where we'll fetch data and render our table.

## Set up Table Features

TanStack Table v9 is feature-based: you opt into the behavior you want — sorting, filtering, pagination, and so on — by declaring it with `tableFeatures()`. Anything you don't register is tree-shaken out of your bundle. That includes the built-in filter and sort functions: register the ones your columns rely on under `filterFns` and `sortFns`. Our email filter uses `includesString`, and string columns sort with `alphanumeric` / `text`.

We'll define the `features` object once in `features.ts` and share it between our column definitions and the `<DataTable />` component.

```ts showLineNumbers
// components/payments/features.ts
import {
  columnFilteringFeature,
  columnVisibilityFeature,
  createExpandedRowModel,
  createFilteredRowModel,
  createPaginatedRowModel,
  createSortedRowModel,
  filterFn_includesString,
  rowExpandingFeature,
  rowPaginationFeature,
  rowSelectionFeature,
  rowSortingFeature,
  sortFn_alphanumeric,
  sortFn_text,
  tableFeatures,
} from '@tanstack/vue-table'

// New in v9: declare the features this table uses — anything you don't
// register is tree-shaken out of the bundle.
export const features = tableFeatures({
  columnFilteringFeature,
  columnVisibilityFeature,
  rowExpandingFeature,
  rowPaginationFeature,
  rowSelectionFeature,
  rowSortingFeature,
  expandedRowModel: createExpandedRowModel(),
  filteredRowModel: createFilteredRowModel(),
  paginatedRowModel: createPaginatedRowModel(),
  sortedRowModel: createSortedRowModel(),
  filterFns: { includesString: filterFn_includesString },
  sortFns: { alphanumeric: sortFn_alphanumeric, text: sortFn_text },
})

// Pass this as the first generic argument to `ColumnDef`, `Column`, `Table`,
// and `Row` so each type knows which feature APIs are available.
export type DataTableFeatures = typeof features
```

::callout{class="mt-4"}

**Note:** The core row model is always included, so you never register it yourself. Row models for optional features are created with `create*RowModel()` and registered on the features object — there are no more `get*RowModel` table options.

::

## Basic Table

Let's start by building a basic table.

<Steps>

### Column Definitions

First, we'll define our columns in the `columns.ts` file using a column helper typed with our features.

```ts showLineNumbers
// components/payments/columns.ts
import { createColumnHelper } from '@tanstack/vue-table'
import type { DataTableFeatures } from './features'

// This type defines the shape of our data.
// You can use a Zod schema here if you want.
export interface Payment {
  id: string
  amount: number
  status: 'pending' | 'processing' | 'success' | 'failed'
  email: string
}

// Use `accessor` for data columns and `display` for columns without one.
const columnHelper = createColumnHelper<DataTableFeatures, Payment>()

export const columns = columnHelper.columns([
  columnHelper.accessor('status', {
    header: 'Status',
  }),
  columnHelper.accessor('email', {
    header: 'Email',
  }),
  columnHelper.accessor('amount', {
    header: 'Amount',
  }),
])
```

::callout{class="mt-4"}

**Note:** Columns are where you define the core of what your table
will look like. They define the data that will be displayed, how it will be
formatted, sorted and filtered.

::

### `<DataTable />` component

Next, we'll create a `<DataTable />` component to render our table.

`useTable` must be called during component setup so it can wire itself into Vue's reactivity. From there the table manages its own state: reads like `table.getRowModel()` in your template are reactive, so your markup updates automatically — no refs or change handlers required. We pass `data` and `columns` through getters so the table always reads the current prop values.

```
/* Detailed source-code truncated for AI context efficiency. */
```

`<FlexRender />` takes the `header` or `cell` instance you pass it and renders whatever the column definition provides — a plain string, a render function, or a component.

::callout

**Tip**: If you find yourself using `<DataTable />` in multiple places, this is the component you could make reusable by extracting it to `components/ui/data-table.vue`.

`<DataTable :columns="columns" :data="data" />`

::

::callout{class="mt-4"}

**Controlled state:** because v9 owns table state internally, you won't write refs or `on*Change` handlers for most of this guide. If something outside the table needs to own a state slice (syncing filters to the URL, server-driven pagination, etc.), controlled state still exists — we'll manage the row selection slice externally in the [Row Selection](#row-selection) section. See the [migration guide](https://tanstack.com/table/latest/docs/framework/vue/guide/migrating) for details.

::

### Render the table

Finally, we'll render our table in our index component.

```vue
<script setup lang="ts">
import type { Payment } from './components/payments/columns'
import { onMounted, ref } from 'vue'
import { columns } from './components/payments/columns'
import DataTable from './components/payments/data-table.vue'

const data = ref<Payment[]>([])

async function getData(): Promise<Payment[]> {
  // Fetch data from your API here.
  return [
    {
      id: '728ed52f',
      amount: 100,
      status: 'pending',
      email: 'm@example.com',
    },
    // ...
  ]
}

onMounted(async () => {
  data.value = await getData()
})
</script>

<template>
  <div class="container py-10 mx-auto">
    <DataTable :columns="columns" :data="data" />
  </div>
</template>
```

</Steps>

## Cell Formatting

Let's format the amount cell to display the dollar amount. We'll also align the cell to the right.

<Steps>

### Update columns definition

Update the `header` and `cell` definitions for amount as follows:

```ts
// components/payments/columns.ts
import { h } from 'vue'

export const columns = columnHelper.columns([
  // ...
  columnHelper.accessor('amount', {
    header: () => h('div', { class: 'text-right' }, 'Amount'),
    cell: ({ row }) => {
      const amount = Number.parseFloat(row.getValue('amount'))

      // Format the amount as a dollar amount
      const formatted = new Intl.NumberFormat('en-US', {
        style: 'currency',
        currency: 'USD',
      }).format(amount)

      return h('div', { class: 'text-right font-medium' }, formatted)
    },
  }),
])
```
You can use the same approach to format other cells and headers.
</Steps>

## Row Actions

Let's add row actions to our table. We'll use a `<Dropdown />` component for this.

<Steps>

### Add the following into your `DataTableDropDown.vue` component

```vue
<script setup lang="ts">
import { MoreHorizontal } from '@lucide/vue'
import { Button } from '@/components/ui/button'
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'

defineProps<{
  payment: {
    id: string
  }
}>()

function copy(id: string) {
  navigator.clipboard.writeText(id)
}
</script>

<template>
  <DropdownMenu>
    <DropdownMenuTrigger as-child>
      <Button variant="ghost" class="w-8 h-8 p-0">
        <span class="sr-only">Open menu</span>
        <MoreHorizontal class="w-4 h-4" />
      </Button>
    </DropdownMenuTrigger>
    <DropdownMenuContent align="end">
      <DropdownMenuLabel>Actions</DropdownMenuLabel>
      <DropdownMenuItem @click="copy(payment.id)">
        Copy payment ID
      </DropdownMenuItem>
      <DropdownMenuSeparator />
      <DropdownMenuItem>View customer</DropdownMenuItem>
      <DropdownMenuItem>View payment details</DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</template>
```

### Update columns definition

Update our columns definition to add a new `actions` column. The `actions` cell returns a `<Dropdown />` component. Because the column doesn't read a data field, we define it with `columnHelper.display`.

```ts
// components/payments/columns.ts
import DropdownAction from '@/components/DataTableDropDown.vue'

export const columns = columnHelper.columns([
  // ...
  columnHelper.display({
    id: 'actions',
    enableHiding: false,
    cell: ({ row }) => {
      const payment = row.original

      return h('div', { class: 'relative' }, h(DropdownAction, {
        payment,
      }))
    },
  }),
])
```

You can access the row data using `row.original` in the `cell` function. Use this to handle actions for your row eg. use the `id` to make a DELETE call to your API.

</Steps>

## Pagination

Next, we'll add pagination to our table.

<Steps>

### Pagination is already enabled

Because our features object registers `rowPaginationFeature` and `createPaginatedRowModel()`, the table automatically paginates rows into pages of 10 — there's nothing to add to `useTable`. See the [pagination docs](https://tanstack.com/table/latest/docs/framework/vue/guide/pagination) for more information on customizing page size and implementing manual pagination.

### Add pagination controls

We can add pagination controls to our table using the `<Button />` component and the `table.previousPage()`, `table.nextPage()` API methods.

```vue
<script setup lang="ts" generic="TData">
import { Button } from '@/components/ui/button'

// ...the rest of the script stays the same
</script>

<template>
  <div>
    <div class="border rounded-md">
      <Table>
        { // .... }
      </Table>
    </div>
    <div class="flex items-center justify-end py-4 space-x-2">
      <Button
        variant="outline"
        size="sm"
        :disabled="!table.getCanPreviousPage()"
        @click="table.previousPage()"
      >
        Previous
      </Button>
      <Button
        variant="outline"
        size="sm"
        :disabled="!table.getCanNextPage()"
        @click="table.nextPage()"
      >
        Next
      </Button>
    </div>
  </div>
</template>
```

See [Reusable Components](#reusable-components) section for a more advanced pagination component.

### Change the page size

To change the page size, call `table.setPageSize()`. To read the current pagination state — say, for a page indicator — read `table.atoms.pagination.get()`.

```vue
<script setup lang="ts">
import { computed } from 'vue'

// Reactive read — updates whenever the user changes pages.
const pagination = computed(() => table.atoms.pagination.get())
</script>

<template>
  <Button variant="outline" size="sm" @click="table.setPageSize(20)">
    Show 20 rows
  </Button>

  <div class="text-sm text-muted-foreground">
    Page {{ pagination.pageIndex + 1 }} of {{ table.getPageCount() }}
  </div>
</template>
```

::callout{class="mt-4"}

**Note:** Atom reads like `table.atoms.pagination.get()` are reactive inside Vue tracking scopes — a template expression or a `computed`. A bare `.get()` at the top level of `<script setup>` is a one-time snapshot, so wrap script-side reads in `computed()`.

::

</Steps>

## Sorting

Let's make the email column sortable.

The `rowSortingFeature` and sorted row model are already registered in our features object — along with the `alphanumeric` and `text` sort functions that string columns resolve through the default `auto` setting — so there's nothing to change in `<DataTable />`. We just add the UI.

<Steps>

### Make header cell sortable

We can update the `email` header cell to add sorting controls.

```ts showLineNumbers {2-3,8-13}
// components/payments/columns.ts
import { ArrowUpDown } from '@lucide/vue'
import { Button } from '@/components/ui/button'

export const columns = columnHelper.columns([
  // ...
  columnHelper.accessor('email', {
    header: ({ column }) => {
      return h(Button, {
        variant: 'ghost',
        onClick: () => column.toggleSorting(column.getIsSorted() === 'asc'),
      }, () => ['Email', h(ArrowUpDown, { class: 'ml-2 h-4 w-4' })])
    },
    cell: ({ row }) => h('div', { class: 'lowercase' }, row.getValue('email')),
  }),
])
```

This will automatically sort the table (asc and desc) when the user toggles on the header cell. The table owns the sorting state — no wiring required.

</Steps>

## Filtering

Let's add a search input to filter emails in our table.

The `columnFilteringFeature`, filtered row model, and the `includesString` filter function are already registered in our features object, so the only work left is rendering an input.

<Steps>

### Add the search input

```vue
<script setup lang="ts" generic="TData">
import { Input } from '@/components/ui/input'

// ...the rest of the script stays the same
</script>

<template>
  <div>
    <div class="flex items-center py-4">
      <Input
        class="max-w-sm"
        placeholder="Filter emails..."
        :model-value="table.getColumn('email')?.getFilterValue() as string"
        @update:model-value="table.getColumn('email')?.setFilterValue($event)"
      />
    </div>
    <div class="border rounded-md">
      <Table>{ ... }</Table>
    </div>
  </div>
</template>
```

Filtering is now enabled for the `email` column. You can add filters to other columns as well — just remember that string-based filter references only resolve functions you've registered, so if another column needs a different built-in filter, add it to `filterFns` in `features.ts` first. See the [filtering docs](https://tanstack.com/table/latest/docs/framework/vue/guide/column-filtering) for more information on customizing filters.

</Steps>

## Visibility

Adding column visibility is fairly simple using `@tanstack/vue-table` visibility API. The `columnVisibilityFeature` is already registered in our features object, so we only need to add the dropdown.

<Steps>

### Add the column toggle dropdown

```vue
<script setup lang="ts" generic="TData">
import { ChevronDown } from '@lucide/vue'
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'

// ...the rest of the script stays the same
</script>

<template>
  <div>
    <div class="flex items-center py-4">
      <Input
        class="max-w-sm"
        placeholder="Filter emails..."
        :model-value="table.getColumn('email')?.getFilterValue() as string"
        @update:model-value="table.getColumn('email')?.setFilterValue($event)"
      />
      <DropdownMenu>
        <DropdownMenuTrigger as-child>
          <Button variant="outline" class="ml-auto">
            Columns <ChevronDown class="w-4 h-4 ml-2" />
          </Button>
        </DropdownMenuTrigger>
        <DropdownMenuContent align="end">
          <DropdownMenuCheckboxItem
            v-for="column in table.getAllColumns().filter((column) => column.getCanHide())"
            :key="column.id"
            class="capitalize"
            :model-value="column.getIsVisible()"
            @update:model-value="(value) => {
              column.toggleVisibility(!!value)
            }"
          >
            {{ column.id }}
          </DropdownMenuCheckboxItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </div>
    <div class="border rounded-md">
      <Table>{ ... }</Table>
    </div>
  </div>
</template>
```

This adds a dropdown menu that you can use to toggle column visibility.

</Steps>

## Row Selection

Next, we're going to add row selection to our table. The `rowSelectionFeature` is already registered in our features object, so the table tracks selection for us — we just render the checkboxes.

<Steps>

### Update column definitions

```ts showLineNumbers {2,5-19}
// components/payments/columns.ts
import { Checkbox } from '@/components/ui/checkbox'

export const columns = columnHelper.columns([
  columnHelper.display({
    id: 'select',
    header: ({ table }) => h(Checkbox, {
      'modelValue': table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && 'indeterminate'),
      'onUpdate:modelValue': (value: boolean | 'indeterminate') => table.toggleAllPageRowsSelected(!!value),
      'ariaLabel': 'Select all',
    }),
    cell: ({ row }) => h(Checkbox, {
      'modelValue': row.getIsSelected(),
      'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value),
      'ariaLabel': 'Select row',
    }),
    enableSorting: false,
    enableHiding: false,
  }),
  // ...
])
```

This adds a checkbox to each row and a checkbox in the header to select all rows.

::callout{class="mt-4"}

**Note:** In v9, `table.getIsSomePageRowsSelected()` returns `true` whenever at least one page row is selected — even when all are. The header checkbox still works because `table.getIsAllPageRowsSelected() || ...` short-circuits to `true` at full selection before the `'indeterminate'` branch is reached.

::

### Manage the selection state externally

The table tracks selection internally by default. To show how a slice can live outside the table — handy when your page needs to read or drive the selection — we'll own just this one slice with a Vue `ref` and leave everything else internal. Expose the current value with a `state` getter, and resolve the updater in the matching callback:

```ts showLineNumbers {2-3,5-7,13-18}
// components/payments/data-table.vue
import type { RowSelectionState } from '@tanstack/vue-table'
import { ref } from 'vue'

// Keep row selection outside the table so the rest of the app can read or update it.
const rowSelection = ref<RowSelectionState>({})

const table = useTable({
  features,
  get data() { return props.data },
  get columns() { return props.columns },
  state: {
    get rowSelection() { return rowSelection.value },
  },
  onRowSelectionChange: (updater) => {
    rowSelection.value = typeof updater === 'function' ? updater(rowSelection.value) : updater
  },
})
```

`rowSelection.value` can now be read or written anywhere in the component, and the table stays in sync.

### Show selected rows

You can show the number of selected rows using the `table.getFilteredSelectedRowModel()` API.

```vue showLineNumbers {8-11}
<template>
  <div>
    <div class="border rounded-md">
        <Table />
    </div>

    <div class="flex items-center justify-end space-x-2 py-4">
      <div class="flex-1 text-sm text-muted-foreground">
        {{ table.getFilteredSelectedRowModel().rows.length }} of
        {{ table.getFilteredRowModel().rows.length }} row(s) selected.
      </div>
      <div class="space-x-2">
        <PaginationButtons />
      </div>
    </div>
  </div>
</template>
```

</Steps>

## Expanding

Let's make rows expandable. The `rowExpandingFeature` and expanded row model are already registered in our features object, so the table tracks expanded state for us — we just add the UI.

<Steps>

### Update `<DataTable>`

Update the `<TableBody>` to render an extra row whenever a row is expanded:

```vue showLineNumbers {3,9-13}
<TableBody>
  <template v-if="table.getRowModel().rows?.length">
    <template v-for="row in table.getRowModel().rows" :key="row.id">
      <TableRow :data-state="row.getIsSelected() && 'selected'">
        <TableCell v-for="cell in row.getVisibleCells()" :key="cell.id">
          <FlexRender :cell="cell" />
        </TableCell>
      </TableRow>
      <TableRow v-if="row.getIsExpanded()">
        <TableCell :colspan="row.getAllCells().length">
          {{ JSON.stringify(row.original) }}
        </TableCell>
      </TableRow>
    </template>
  </template>
  <template v-else>
    <TableRow>
      <TableCell :colspan="columns.length" class="h-24 text-center">
        No results.
      </TableCell>
    </TableRow>
  </template>
</TableBody>
```

### Add the expand action to the `DataTableDropDown.vue` component

```vue showLineNumbers {12-14,34-36}
<script setup lang="ts">
import { MoreHorizontal } from '@lucide/vue'
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'
import { Button } from '@/components/ui/button'

defineProps<{
  payment: {
    id: string
  }
}>()

defineEmits<{
  (e: 'expand'): void
}>()

function copy(id: string) {
  navigator.clipboard.writeText(id)
}
</script>

<template>
  <DropdownMenu>
    <DropdownMenuTrigger as-child>
      <Button variant="ghost" class="w-8 h-8 p-0">
        <span class="sr-only">Open menu</span>
        <MoreHorizontal class="w-4 h-4" />
      </Button>
    </DropdownMenuTrigger>
    <DropdownMenuContent align="end">
      <DropdownMenuLabel>Actions</DropdownMenuLabel>
      <DropdownMenuItem @click="copy(payment.id)">
        Copy payment ID
      </DropdownMenuItem>
      <DropdownMenuItem @click="$emit('expand')">
        Expand
      </DropdownMenuItem>
      <DropdownMenuSeparator />
      <DropdownMenuItem>View customer</DropdownMenuItem>
      <DropdownMenuItem>View payment details</DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</template>
```

### Make rows expandable

Now we can update the action cell to add the expand control.

```ts showLineNumbers {12}
// components/payments/columns.ts
export const columns = columnHelper.columns([
  // ...
  columnHelper.display({
    id: 'actions',
    enableHiding: false,
    cell: ({ row }) => {
      const payment = row.original

      return h('div', { class: 'relative' }, h(DropdownAction, {
        payment,
        onExpand: () => row.toggleExpanded(),
      }))
    },
  }),
])
```

</Steps>

## Reusable Components

Here are some components you can use to build your data tables. This is from the [Tasks](/examples/tasks) demo.

### Column header

Make any column header sortable and hideable.

```
/* Detailed source-code truncated for AI context efficiency. */
```

```ts showLineNumbers
export const columns = columnHelper.columns([
  columnHelper.accessor('email', {
    header: ({ column }) => (
      h(DataTableColumnHeader, {
        column,
        title: 'Email',
      })
    ),
  }),
])
```

### Pagination

Add pagination controls to your table including page size and selection count.

```
/* Detailed source-code truncated for AI context efficiency. */
```

```vue
<DataTablePagination :table="table" />
```

### Column toggle

A component to toggle column visibility.

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Apps V4 Content Docs Components Date Picker (`apps/v4/content/docs/components/date-picker.md`)

---
title: Date Picker
description: A date picker component with range and presets.
component: true
links:
  doc: https://reka-ui.com/docs/components/date-picker
  api: https://reka-ui.com/docs/components/date-picker#api-reference
---

::component-preview
---
name: DatePickerDemo
description: A date picker component.
---
::

## Installation

The Date Picker is built using a composition of the `<Popover />` and the `<Calendar />` components.

See installation instructions for the [Popover](/docs/components/popover) and the [Calendar](/docs/components/calendar) components.

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { DateFormatter, getLocalTimeZone, today } from '@internationalized/date'
import { CalendarIcon } from '@lucide/vue'
import { ref } from 'vue'
import { Button } from '@/components/ui/button'
import { Calendar } from '@/components/ui/calendar'
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from '@/components/ui/popover'
import { cn } from '@/lib/utils'

const date = ref<Date>()
const defaultPlaceholder = today(getLocalTimeZone())
</script>

<template>
  <Popover>
    <PopoverTrigger as-child>
      <Button
        variant="outline"
        :class="cn(
          'w-[280px] justify-start text-left font-normal',
          !date && 'text-muted-foreground',
        )"
      >
        <CalendarIcon class="mr-2 h-4 w-4" />
        {{ date ? date.toDateString() : "Pick a date" }}
      </Button>
    </PopoverTrigger>
    <PopoverContent class="w-auto p-0">
      <Calendar
        v-model="date"
        :initial-focus="true"
        :default-placeholder="defaultPlaceholder"
        layout="month-and-year"
      />
    </PopoverContent>
  </Popover>
</template>
```

---

### Apps V4 Content Docs Components Dialog (`apps/v4/content/docs/components/dialog.md`)

---
title: Dialog
description: A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
component: true
links:
  doc: https://reka-ui.com/docs/components/dialog
  api: https://reka-ui.com/docs/components/dialog#api-reference
---

::component-preview
---
name: DialogDemo
description: A dialog with a form.
---
::

::vue-school-link{class="mt-6" lesson="dialogs-and-alerts-with-shadcn-vue" placement="top"}
Watch a Vue School video about dialogs and alerts in shadcn-vue.
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add dialog
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/dialog) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from '@/components/ui/dialog'
</script>

<template>
  <Dialog>
    <DialogTrigger>Open</DialogTrigger>
    <DialogContent>
      <DialogHeader>
        <DialogTitle>Are you absolutely sure?</DialogTitle>
        <DialogDescription>
          This action cannot be undone. This will permanently delete your account
          and remove your data from our servers.
        </DialogDescription>
      </DialogHeader>
    </DialogContent>
  </Dialog>
</template>
```

## Example

### Custom close button

::component-preview
---
name: DialogCloseButton
---
::

### Dialog with form

To keep field value after `Dialog` unmounts

::component-preview
---
name: DialogForm
---
::

### Responsive Modal (Dialog & Drawer)

Use a `Drawer` component for smaller viewport sizes and a `Dialog` component otherwise. This can be further made reusable by using slots for various parts of the modal.

::component-preview
---
name: DialogResponsive
---
::

## Notes

To use the `Dialog` component from within a `Context Menu` or `Dropdown Menu`, you must encase the `Context Menu` or
`Dropdown Menu` component in the `Dialog` component.

```vue showLineNumbers title="components/EampleDialogContext.vue" {2, 29}
<template>
  <Dialog>
    <ContextMenu>
      <ContextMenuTrigger>Right click</ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuItem>Open</ContextMenuItem>
        <ContextMenuItem>Download</ContextMenuItem>
        <DialogTrigger as-child>
          <ContextMenuItem>
            <span>Delete</span>
          </ContextMenuItem>
        </DialogTrigger>
      </ContextMenuContent>
    </ContextMenu>
    <DialogContent>
      <DialogHeader>
        <DialogTitle>Are you absolutely sure?</DialogTitle>
        <DialogDescription>
          This action cannot be undone. Are you sure you want to permanently
          delete this file from our servers?
        </DialogDescription>
      </DialogHeader>
      <DialogFooter>
        <Button type="submit">
          Confirm
        </Button>
      </DialogFooter>
    </DialogContent>
  </Dialog>
</template>
```

---

### Apps V4 Content Docs Components Drawer (`apps/v4/content/docs/components/drawer.md`)

---
title: Drawer
description: A drawer component for mobile devices.
component: true
links:
  doc: https://reka-ui.com/docs/components/drawer
  api: https://reka-ui.com/docs/components/drawer#api-reference
---

::component-preview
---
name: DrawerDemo
description: A drawer component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add drawer
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/drawer) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Drawer,
  DrawerClose,
  DrawerContent,
  DrawerDescription,
  DrawerFooter,
  DrawerHeader,
  DrawerTitle,
  DrawerTrigger,
} from '@/components/ui/drawer'
</script>

<template>
  <Drawer>
    <DrawerTrigger>Open</DrawerTrigger>
    <DrawerContent>
      <DrawerHeader>
        <DrawerTitle>Are you absolutely sure?</DrawerTitle>
        <DrawerDescription>
          This action cannot be undone.
        </DrawerDescription>
      </DrawerHeader>
      <DrawerFooter>
        <Button>Submit</Button>
        <DrawerClose>
          <Button variant="outline">
            Cancel
          </Button>
        </DrawerClose>
      </DrawerFooter>
    </DrawerContent>
  </Drawer>
</template>
```

## Example

### Scrollable Content

Keep actions visible while the content scrolls.

::component-preview
---
name: DrawerScrollableContent
---
::

### Sides

Use the `swipe-direction` prop to set the direction the drawer swipes toward to dismiss. The drawer is anchored to the edge it dismisses toward, so `up` opens a drawer along the top edge and `down` opens one along the bottom edge. Available options are `up`, `right`, `down`, and `left`.

::component-preview
---
name: DrawerSides
---
::

### Responsive Modal (Dialog & Drawer)

Use a `Drawer` component for smaller viewport sizes and a `Dialog` component otherwise. This can be further made reusable by using slots for various parts of the modal.

::component-preview
---
name: DialogResponsive
---
::

---

### Apps V4 Content Docs Components Dropdown Menu (`apps/v4/content/docs/components/dropdown-menu.md`)

---
title: Dropdown Menu
description: Displays a menu to the user — such as a set of actions or functions — triggered by a button.
component: true
links:
  doc: https://reka-ui.com/docs/components/dropdown-menu
  api: https://reka-ui.com/docs/components/dropdown-menu#api-reference
---

::component-preview
---
name: DropdownMenuDemo
description: A dropdown menu with actions.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add dropdown-menu
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/dropdown-menu) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
</script>

<template>
  <DropdownMenu>
    <DropdownMenuTrigger>Open</DropdownMenuTrigger>
    <DropdownMenuContent>
      <DropdownMenuLabel>My Account</DropdownMenuLabel>
      <DropdownMenuSeparator />
      <DropdownMenuItem>Profile</DropdownMenuItem>
      <DropdownMenuItem>Billing</DropdownMenuItem>
      <DropdownMenuItem>Team</DropdownMenuItem>
      <DropdownMenuItem>Subscription</DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</template>
```

---

### Apps V4 Content Docs Components Empty (`apps/v4/content/docs/components/empty.md`)

---
title: Empty
description: Use the Empty component to display an empty state.
component: true
---

::component-preview
---
name: EmptyDemo
previewClass: h-96 p-0
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add empty
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/empty) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { FolderOpen } from '@lucide/vue'
import { Button } from '@/registry/default/ui/button'
import {
  Empty,
  EmptyContent,
  EmptyDescription,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
} from '@/registry/default/ui/empty'
</script>

<template>
  <Empty>
    <EmptyHeader>
      <EmptyMedia variant="icon">
        <FolderOpen />
      </EmptyMedia>
    </EmptyHeader>
    <EmptyTitle>No data</EmptyTitle>
    <EmptyDescription>No data found</EmptyDescription>
    <EmptyContent>
      <Button>Add data</Button>
    </EmptyContent>
  </Empty>
</template>
```

## Examples

### Outline

Use the `border` utility class to create an outline empty state.

::component-preview
---
name: EmptyOutlineDemo
class: '[&_.preview]:p-6 md:[&_.preview]:p-10'
previewClass: h-96
---
::

### Background

Use the `bg-*` and `bg-gradient-*` utilities to add a background to the empty state.

::component-preview
---
name: EmptyBackgroundDemo
class: '[&_.preview]:p-0'
---
::

### Avatar

Use the `EmptyMedia` component to display an avatar in the empty state.

::component-preview
---
name: EmptyAvatarDemo
class: '[&_.preview]:p-0'
previewClass: h-80
---
::

### Avatar Group

Use the `EmptyMedia` component to display an avatar group in the empty state.

::component-preview
---
name: EmptyAvatarGroupDemo
class: '[&_.preview]:p-0'
---
::

### InputGroup

You can add an `InputGroup` component to the `EmptyContent` component.

::component-preview
---
name: EmptyInputGroupDemo
class: '[&_.preview]:p-0'
---
::

## API Reference

### Empty

The main component of the empty state. Wraps the `EmptyHeader` and `EmptyContent` components.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

```vue
<template>
  <Empty>
    <EmptyHeader />
    <EmptyContent />
  </Empty>
</template>
```

### EmptyHeader

The `EmptyHeader` component wraps the empty media, title, and description.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

```vue
<template>
  <EmptyHeader>
    <EmptyMedia />
    <EmptyTitle />
    <EmptyDescription />
  </EmptyHeader>
</template>
```

### EmptyMedia

Use the `EmptyMedia` component to display the media of the empty state such as an icon or an image. You can also use it to display other components such as an avatar.

| Prop        | Type                  | Default   |
| ----------- | --------------------- | --------- |
| `variant`   | `"default" \| "icon"` | `default` |
| `class` | `string`              |           |

```vue
<template>
  <EmptyMedia variant="icon">
    <Icon />
  </EmptyMedia>
</template>
```

```vue
<template>
  <EmptyMedia>
    <Avatar>
      <AvatarImage src="..." />
      <AvatarFallback>CN</AvatarFallback>
    </Avatar>
  </EmptyMedia>
</template>
```

### EmptyTitle

Use the `EmptyTitle` component to display the title of the empty state.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

```vue
<template>
  <EmptyTitle>No data</EmptyTitle>
</template>
```

### EmptyDescription

Use the `EmptyDescription` component to display the description of the empty state.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

```vue
<template>
  <EmptyDescription>You do not have any notifications.</EmptyDescription>
</template>
```

### EmptyContent

Use the `EmptyContent` component to display the content of the empty state such as a button, input or a link.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

```vue
<template>
  <EmptyContent>
    <Button>Add Project</Button>
  </EmptyContent>
</template>
```

---

### Apps V4 Content Docs Components Field (`apps/v4/content/docs/components/field.md`)

---
title: Field
description: Combine labels, controls, and help text to compose accessible form fields and grouped inputs.
component: true
---

::component-preview
---
name: FieldDemo
class: '[&_.preview]:h-[800px] [&_.preview]:p-6 md:[&_.preview]:h-[850px]'
---
::

## Installation

```bash
npx shadcn-vue@latest add field
```

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Field,
  FieldContent,
  FieldDescription,
  FieldError,
  FieldGroup,
  FieldLabel,
  FieldLegend,
  FieldSeparator,
  FieldSet,
  FieldTitle,
} from '@/components/ui/field'
</script>

<template>
  <FieldSet>
    <FieldLegend>Profile</FieldLegend>
    <FieldDescription>This appears on invoices and emails.</FieldDescription>
    <FieldGroup>
      <Field>
        <FieldLabel for="name">
          Full name
        </FieldLabel>
        <Input id="name" autocomplete="off" placeholder="Evil Rabbit" />
        <FieldDescription>This appears on invoices and emails.</FieldDescription>
      </Field>
      <Field>
        <FieldLabel for="username">
          Username
        </FieldLabel>
        <Input id="username" autocomplete="off" aria-invalid />
        <FieldError>Choose another username.</FieldError>
      </Field>
      <Field orientation="horizontal">
        <Switch id="newsletter" />
        <FieldLabel for="newsletter">
          Subscribe to the newsletter
        </FieldLabel>
      </Field>
    </FieldGroup>
  </FieldSet>
</template>
```

## Anatomy

The `Field` family is designed for composing accessible forms. A typical field is structured as follows:

```vue showLineNumbers
<template>
  <Field>
    <FieldLabel for="input-id">
      Label
    </FieldLabel>
    
    <FieldDescription>Optional helper text.</FieldDescription>
    <FieldError>Validation message.</FieldError>
  </Field>
</template>
```

- `Field` is the core wrapper for a single field.
- `FieldContent` is a flex column that groups label and description. Not required if you have no description.
- Wrap related fields with `FieldGroup`, and use `FieldSet` with `FieldLegend` for semantic grouping.

## Examples

### Input

::component-preview
---
name: FieldInputDemo
class: '!mb-4 [&_.preview]:p-6'
previewClass: h-80
---
::

### Textarea

::component-preview
---
name: FieldTextareaDemo
class: '!mb-4 [&_.preview]:p-6'
---
::

### Select

::component-preview
---
name: FieldSelectDemo
class: '!mb-4 [&_.preview]:p-6'
---
::

### Slider

::component-preview
---
name: FieldSliderDemo
class: '!mb-4 [&_.preview]:p-6'
---
::

### Fieldset

::component-preview
---
name: FieldFieldsetDemo
class: '!mb-4 [&_.preview]:p-6'
previewClass: h-80 sm:h-72
---
::

### Checkbox

::component-preview
---
name: FieldCheckboxDemo
class: '!mb-4 [&_.preview]:p-6'
previewClass: h-[26rem] sm:h-96
---
::

### Radio

::component-preview
---
name: FieldRadioDemo
class: '!mb-4 [&_.preview]:p-6'
---
::

### Switch

::component-preview
---
name: FieldSwitchDemo
class: '!mb-4 [&_.preview]:p-6'
---
::

### Choice Card

Wrap `Field` components inside `FieldLabel` to create selectable field groups. This works with `RadioItem`, `Checkbox` and `Switch` components.

::component-preview
---
name: FieldChoiceCardDemo
class: '!mb-4 [&_.preview]:p-6'
previewClass: h-88 sm:h-72
---
::

### Field Group

Stack `Field` components with `FieldGroup`. Add `FieldSeparator` to divide them.

::component-preview
---
name: FieldGroupDemo
class: '!mb-4 [&_.preview]:p-6'
previewClass: h-96 sm:h-88
---
::

## Responsive Layout

If you are in tailwindcss v3 you need to install [`@tailwindcss/container-queries`](https://github.com/tailwindlabs/tailwindcss-container-queries)

- **Vertical fields:** Default orientation stacks label, control, and helper text—ideal for mobile-first layouts.
- **Horizontal fields:** Set `orientation="horizontal"` on `Field` to align the label and control side-by-side. Pair with `FieldContent` to keep descriptions aligned.
- **Responsive fields:** Set `orientation="responsive"` for automatic column layouts inside container-aware parents. Apply `@container/field-group` classes on `FieldGroup` to switch orientations at specific breakpoints.

::component-preview
---
name: FieldResponsiveDemo
class: '!mb-4 [&_.preview]:h-[650px] [&_.preview]:p-6 [&_.preview]:md:h-[500px] [&_.preview]:md:p-10'
---
::

## Validation and Errors

- Add `data-invalid` to `Field` to switch the entire block into an error state.
- Add `aria-invalid` on the input itself for assistive technologies.
- Render `FieldError` immediately after the control or inside `FieldContent` to keep error messages aligned with the field.

```vue showLineNumbers /data-invalid/ /aria-invalid/
<template>
  <Field data-invalid>
    <FieldLabel for="email">
      Email
    </FieldLabel>
    <Input id="email" type="email" aria-invalid />
    <FieldError>Enter a valid email address.</FieldError>
  </Field>
</template>
```

## Accessibility

  - `FieldSet` and `FieldLegend` keep related controls grouped for keyboard and assistive tech users.
  - `Field` outputs `role="group"` so nested controls inherit labeling from `FieldLabel` and `FieldLegend` when combined.
  - Apply `FieldSeparator` sparingly to ensure screen readers encounter clear section boundaries.

## API Reference

### FieldSet

Container that renders a semantic `fieldset` with spacing presets.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

```vue
<template>
  <FieldSet>
    <FieldLegend>Delivery</FieldLegend>
    <FieldGroup>
      
    </FieldGroup>
  </FieldSet>
</template>
```

### FieldLegend

Legend element for a `FieldSet`. Switch to the `label` variant to align with label sizing.

| Prop        | Type                  | Default    |
| ----------- | --------------------- | ---------- |
| `variant`   | `"legend" \| "label"` | `"legend"` |
| `class` | `string`              |            |

```vue
<FieldLegend variant="label">
Notification Preferences
</FieldLegend>
```

The `FieldLegend` has two variants: `legend` and `label`. The `label` variant applies label sizing and alignment. Handy if you have nested `FieldSet`.

### FieldGroup

Layout wrapper that stacks `Field` components and enables container queries for responsive orientations.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

```vue
<template>
  <FieldGroup class="@container/field-group flex flex-col gap-6">
    <Field></Field>
    <Field></Field>
  </FieldGroup>
</template>
```

### Field

The core wrapper for a single field. Provides orientation control, invalid state styling, and spacing.

| Prop           | Type                                         | Default      |
| -------------- | -------------------------------------------- | ------------ |
| `orientation`  | `"vertical" \| "horizontal" \| "responsive"` | `"vertical"` |
| `class`    | `string`                                     |              |
| `data-invalid` | `boolean`                                    |              |

```vue
<Field orientation="horizontal">
  <FieldLabel for="remember">Remember me</FieldLabel>
  <Switch id="remember" />
</Field>
```

### FieldContent

Flex column that groups control and descriptions when the label sits beside the control. Not required if you have no description.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

```vue
<Field>
  <Checkbox id="notifications" />
  <FieldContent>
    <FieldLabel for="notifications">Notifications</FieldLabel>
    <FieldDescription>Email, SMS, and push options.</FieldDescription>
  </FieldContent>
</Field>
```

### FieldLabel

Label styled for both direct inputs and nested `Field` children.

| Prop        | Type      | Default |
| ----------- | --------- | ------- |
| `class` | `string`  |         |
| `asChild`   | `boolean` | `false` |

```vue
<FieldLabel for="email">
Email
</FieldLabel>
```

### FieldTitle

Renders a title with label styling inside `FieldContent`.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

```vue
<FieldContent>
  <FieldTitle>Enable Touch ID</FieldTitle>
  <FieldDescription>Unlock your device faster.</FieldDescription>
</FieldContent>
```

### FieldDescription

Helper text slot that automatically balances long lines in horizontal layouts.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

```vue
<FieldDescription>
We never share your email with anyone.
</FieldDescription>
```

### FieldSeparator

Visual divider to separate sections inside a `FieldGroup`. Accepts optional inline content.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

```vue
<FieldSeparator>
Or continue with
</FieldSeparator>
```

### FieldError

Accessible error container that accepts children or an `errors` array (e.g., from `vee-validate`).

| Prop        | Type                                       | Default |
| ----------- | ------------------------------------------ | ------- |
| `errors`    | `Array<{ message?: string } \| undefined>` |         |
| `class` | `string`                                   |         |

```vue
<FieldError :errors="errors.username" />
```

When the `errors` array contains multiple messages, the component renders a list automatically.

`FieldError` also accepts issues produced by any validator that implements [Standard Schema](https://standardschema.dev/), including Zod, Valibot, and ArkType. Pass the `issues` array from the schema result directly to render a unified error list across libraries.

---

### Apps V4 Content Docs Components Form (`apps/v4/content/docs/components/form.md`)

---
title: Form
description: Building forms with VeeValidate and Zod.
primitive: https://vee-validate.logaretm.com/v4/guide/overview/
component: true
---

::callout{title="We are not actively developing this component anymore."}

The Form component is an abstraction over the `vee-validate` library. Going forward, we recommend using the [`<Field />`](/docs/components/field) component to build forms. See the [Form](/docs/forms) documentation for more information.

::

Forms are tricky. They are one of the most common things you'll build in a web application, but also one of the most complex.

Well-designed HTML forms are:

- Well-structured and semantically correct.
- Easy to use and navigate (keyboard).
- Accessible with ARIA attributes and proper labels.
- Has support for client and server side validation.
- Well-styled and consistent with the rest of the application.

In this guide, we will take a look at building forms with [`vee-validate`](https://vee-validate.logaretm.com/v4/) and [`zod`](https://zod.dev). We're going to use a `<FormField>` component to compose accessible forms using Reka UI components.

## Features

The `<Form />` component is a wrapper around the `vee-validate` library. It provides a few things:

- Composable components for building forms.
- A `<FormField />` component for building controlled form fields.
- Form validation using `zod`.
- Applies the correct `aria` attributes to form fields based on states, handle unique IDs
- Built to work with all Reka UI components.
- Bring your own schema library. We use `zod` but you can use any other supported schema validation you want, like [`yup`](https://github.com/jquense/yup) or [`valibot`](https://valibot.dev/).
- **You have full control over the markup and styling.**

[`vee-validate`](https://vee-validate.logaretm.com/v4/) makes use of two flavors to add validation to your forms.
- Composition API
- Higher-order components (HOC)

## Anatomy

```vue
<template>
  <Form>
    <FormField>
      <FormItem>
        <FormLabel />
        <FormControl>
        
        </FormControl>
        <FormDescription />
        <FormMessage />
      </FormItem>
    </FormField>
  </Form>
</template>
```

## Example

::::tabs{default-value="component"}

  :::tabs-list

    ::tabs-trigger{value="component"}
    Component
    ::

    ::tabs-trigger{value="native"}
    Native
    ::

  :::

  ::tabs-content{value="component"}

  #### `Input` Component

  ```vue showLineNumbers
  <template>
    <FormField v-slot="{ componentField }">
      <FormItem>
        <FormLabel>Username</FormLabel>
        <FormControl>
          <Input placeholder="shadcn" v-bind="componentField" />
        </FormControl>
        <FormDescription />
        <FormMessage />
      </FormItem>
    </FormField>
  </template>
  ```

  ::

  ::tabs-content{value="native"}

  #### native `input` element

  ```vue showLineNumbers
  <template>
    <FormField v-slot="{ field }">
      <FormItem>
        <FormLabel>Username</FormLabel>
        <FormControl>
          <input placeholder="shadcn" v-bind="field">
        </FormControl>
        <FormDescription />
        <FormMessage />
      </FormItem>
    </FormField>
  </template>
  ```

  ::
::::

## Installation

:::::tabs{default-value="cli"}

  :::tabs-list

    ::tabs-trigger{value="cli"}
    CLI
    ::

    ::tabs-trigger{value="manual"}
    Manual
    ::

  :::

  ::tabs-content{value="cli"}

  ```bash
  npx shadcn-vue@latest add form
  ```

  ::

  ::::tabs-content{value="manual"}

    :::steps
      ::step
      Install the following dependency:
      ::

      ```bash
      npm install reka-ui vee-validate @vee-validate/zod zod
      ```

      ::step
      Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/form) into your project.
      ::

      ::step
      Update the import paths to match your project setup.
      ::
    :::

  ::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/components/ui/form'
</script>

<template>
  <FormField v-slot="{ componentField }" name="username">
    <FormItem>
      <FormLabel>Username</FormLabel>
      <FormControl>
        <Input placeholder="shadcn" v-bind="componentField" />
      </FormControl>
      <FormDescription>
        This is your public display name.
      </FormDescription>
      <FormMessage />
    </FormItem>
  </FormField>
</template>
```

### Create a form schema

  Define the shape of your form using a Zod schema. You can read more about using Zod in the [Zod documentation](https://zod.dev).

  Use `@vee-validate/zod` to integrate Zod schema validation with `vee-validate`

  `toTypedSchema` also makes the form values and submitted values typed automatically and caters for both input and output types of that schema.

  ```vue showLineNumbers {2-3,5-7}
  <script setup lang="ts">
  import { toTypedSchema } from '@vee-validate/zod'
  import * as z from 'zod'

  const formSchema = toTypedSchema(z.object({
    username: z.string().min(2).max(50),
  }))
  </script>
  ```

### Define a form

  Use the `useForm` composable from `vee-validate` or use `<Form />` component to create a form.

  ::::tabs{default-value="composition"}

    :::tabs-list

      ::tabs-trigger{value="composition"}
      Composition
      ::

      ::tabs-trigger{value="component"}
      Component
      ::

    :::

    ::tabs-content{value="composition"}

    ```vue showLineNumbers {2,19-21}
    <script setup lang="ts">
    import { useForm } from 'vee-validate'
    import { toTypedSchema } from '@vee-validate/zod'
    import * as z from 'zod'

    import {
      FormControl,
      FormDescription,
      FormField,
      FormItem,
      FormLabel,
      FormMessage
    } from '@/components/ui/form'

    const formSchema = toTypedSchema(z.object({
      username: z.string().min(2).max(50),
    }))

    const form = useForm({
      validationSchema: formSchema,
    })

    const onSubmit = form.handleSubmit((values) => {
      console.log('Form submitted!', values)
    })
    </script>

    <template>
      <form @submit="onSubmit">
        ...
      </form>
    </template>
    ```

    ::

    ::tabs-content{value="component"}

    ```vue showLineNumbers {5,24-26}
    <script setup lang="ts">
    import { toTypedSchema } from '@vee-validate/zod'
    import * as z from 'zod'
    import {
      Form,
      FormControl,
      FormDescription,
      FormField,
      FormItem,
      FormLabel,
      FormMessage
    } from '@/components/ui/form'

    const formSchema = toTypedSchema(z.object({
      username: z.string().min(2).max(50),
    }))

    function onSubmit(values) {
      console.log('Form submitted!', values)
    }
    </script>

    <template>
      <Form :validation-schema="formSchema" @submit="onSubmit">
        ...
      </Form>
    </template>
    ```

    ::

### Build your form

  Based on last step we can either use `<Form />` component or `useForm` composable
  `useForm` is recommended because values are typed automatically

  ```vue showLineNumbers {2}
  <script setup lang="ts">
  import { toTypedSchema } from '@vee-validate/zod'
  import { useForm } from 'vee-validate'
  import * as z from 'zod'

  import { Button } from '@/components/ui/button'
  import {
    FormControl,
    FormDescription,
    FormField,
    FormItem,
    FormLabel,
    FormMessage,
  } from '@/components/ui/form'
  import { Input } from '@/components/ui/input'

  const formSchema = toTypedSchema(z.object({
    username: z.string().min(2).max(50),
  }))

  const form = useForm({
    validationSchema: formSchema,
  })

  const onSubmit = form.handleSubmit((values) => {
    console.log('Form submitted!', values)
  })
  </script>

  <template>
    <form @submit="onSubmit">
      <FormField v-slot="{ componentField }" name="username">
        <FormItem>
          <FormLabel>Username</FormLabel>
          <FormControl>
            <Input type="text" placeholder="shadcn" v-bind="componentField" />
          </FormControl>
          <FormDescription>
            This is your public display name.
          </FormDescription>
          <FormMessage />
        </FormItem>
      </FormField>
      <Button type="submit">
        Submit
      </Button>
    </form>
  </template>
  ```

### Done

  That's it. You now have a fully accessible form that is type-safe with client-side validation.

  ::component-preview
  ---
  name: InputForm
  class: '[&_[role=tablist]]:hidden [&>div>div:first-child]:hidden'
  ---
  ::

::::

---

### Apps V4 Content Docs Components Hover Card (`apps/v4/content/docs/components/hover-card.md`)

---
title: Hover Card
description: For sighted users to preview content available behind a link.
component: true
links:
  doc: https://reka-ui.com/docs/components/hover-card
  api: https://reka-ui.com/docs/components/hover-card#api-reference
---

::component-preview
---
name: HoverCardDemo
description: A hover card component.
previewClass: h-80
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add hover-card
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/hover-card) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from '@/components/ui/hover-card'
</script>

<template>
  <HoverCard>
    <HoverCardTrigger>Hover</HoverCardTrigger>
    <HoverCardContent>
      The Vue Framework – created and maintained by Evan You.
    </HoverCardContent>
  </HoverCard>
</template>
```

---

### Apps V4 Content Docs Components Input Group (`apps/v4/content/docs/components/input-group.md`)

---
title: Input Group
description: Display additional information or actions to an input or textarea.
component: true
---

::component-preview
---
name: InputGroupDemo
class: '[&_.preview]:p-4'
previewClass: h-[26rem]
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add input-group
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/input-group) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
  InputGroupText,
  InputGroupTextarea,
} from '@/components/ui/input-group'
</script>

<template>
  <InputGroup>
    <InputGroupInput placeholder="Search..." />
    <InputGroupAddon>
      <SearchIcon />
    </InputGroupAddon>
    <InputGroupAddon align="inline-end">
      <InputGroupButton>Search</InputGroupButton>
    </InputGroupAddon>
  </InputGroup>
</template>
```

## Examples

### Icon

::component-preview
---
name: InputGroupWithIcon
class: '[&_.preview]:p-4'
---
::

### Text

Display additional text information alongside inputs.

::component-preview
---
name: InputGroupWithText
class: '[&_.preview]:p-4'
previewClass: h-80
---
::

### Button

Add buttons to perform actions within the input group.

::component-preview
---
name: InputGroupWithButton
class: '[&_.preview]:p-4'
---
::

### Tooltip

Add tooltips to provide additional context or help.

::component-preview
---
name: InputGroupWithTooltip
class: '[&_.preview]:p-4'
---
::

### Textarea

Input groups also work with textarea components. Use `block-start` or `block-end` for alignment.

::component-preview
---
name: InputGroupWithTextarea
class: '[&_.preview]:p-4'
previewClass: h-88
---
::

### Spinner

Show loading indicators while processing input.

::component-preview
---
name: InputGroupWithSpinner
class: '[&_.preview]:p-4'
---
::

### Label

Add labels within input groups to improve accessibility.

::component-preview
---
name: InputGroupWithLabel
class: '[&_.preview]:p-4'
---
::

### Dropdown

Pair input groups with dropdown menus for complex interactions.

::component-preview
---
name: InputGroupWithDropdown
class: '[&_.preview]:p-4'
---
::

### Button Group

Wrap input groups with button groups to create prefixes and suffixes.

::component-preview
---
name: InputGroupWithButtonGroup
class: '[&_.preview]:p-4'
---
::

### Custom Input

Add the `data-slot="input-group-control"` attribute to your custom input for automatic behavior and focus state handling.

No style is applied to the custom input. Apply your own styles using the `class` prop.

::component-preview
---
name: InputGroupWithCustomInput
class: '!mb-4 [&_.preview]:p-4'
---
::

```vue showLineNumbers
<script setup lang="ts">
import { InputGroup, InputGroupAddon, InputGroupButton } from '@/components/ui/input-group'
</script>

<template>
  <div class="grid w-full max-w-sm gap-6">
    <InputGroup>
      <textarea
        data-slot="input-group-control"
        class="flex field-sizing-content min-h-16 w-full resize-none rounded-md bg-transparent px-3 py-2.5 text-base transition-[color,box-shadow] outline-none md:text-sm"
        placeholder="Autoresize textarea..."
      />
      <InputGroupAddon align="block-end">
        <InputGroupButton class="ml-auto" size="sm" variant="default">
          Submit
        </InputGroupButton>
      </InputGroupAddon>
    </InputGroup>
  </div>
</template>
```

## API Reference

### InputGroup

The main component that wraps inputs and addons.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

```vue
<InputGroup>
  <InputGroupInput />
  <InputGroupAddon />
</InputGroup>
```

### InputGroupAddon

Displays icons, text, buttons, or other content alongside inputs.

::callout{title="Focus Navigation"}
For proper focus navigation, the `InputGroupAddon` component should be placed
after the input. Set the `align` prop to position the addon.
::

| Prop        | Type                                                             | Default          |
| ----------- | ---------------------------------------------------------------- | ---------------- |
| `align`     | `"inline-start" \| "inline-end" \| "block-start" \| "block-end"` | `"inline-start"` |
| `class` | `string`                                                         |                  |

```vue
<InputGroupAddon align="inline-end">
  <SearchIcon />
</InputGroupAddon>
```

**For `<InputGroupInput />`, use the `inline-start` or `inline-end` alignment. For `<InputGroupTextarea />`, use the `block-start` or `block-end` alignment.**

The `InputGroupAddon` component can have multiple `InputGroupButton` components and icons.

```vue
<InputGroupAddon>
  <InputGroupButton>Button</InputGroupButton>
  <InputGroupButton>Button</InputGroupButton>
</InputGroupAddon>
```

### InputGroupButton

Displays buttons within input groups.

| Prop        | Type                                                                          | Default   |
| ----------- | ----------------------------------------------------------------------------- | --------- |
| `size`      | `"xs" \| "icon-xs" \| "sm" \| "icon-sm"`                                      | `"xs"`    |
| `variant`   | `"default" \| "destructive" \| "outline" \| "secondary" \| "ghost" \| "link"` | `"ghost"` |
| `class` | `string`                                                                      |           |

```vue
<InputGroupButton>
Button
</InputGroupButton>

<InputGroupButton size="icon-xs" aria-label="Copy">
  <CopyIcon />
</InputGroupButton>
```

### InputGroupInput

Replacement for `<Input />` when building input groups. This component has the input group styles pre-applied and uses the unified `data-slot="input-group-control"` for focus state handling.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

All other props are passed through to the underlying `<Input />` component.

```vue
<InputGroup>
  <InputGroupInput placeholder="Enter text..." />
  <InputGroupAddon>
    <SearchIcon />
  </InputGroupAddon>
</InputGroup>
```

### InputGroupTextarea

Replacement for `<Textarea />` when building input groups. This component has the textarea group styles pre-applied and uses the unified `data-slot="input-group-control"` for focus state handling.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

All other props are passed through to the underlying `<Textarea />` component.

```vue
<InputGroup>
  <InputGroupTextarea placeholder="Enter message..." />
  <InputGroupAddon align="block-end">
    <InputGroupButton>Send</InputGroupButton>
  </InputGroupAddon>
</InputGroup>
```

---

### Apps V4 Content Docs Components Input Otp (`apps/v4/content/docs/components/input-otp.md`)

---
title: Input OTP
description: Accessible one-time password component with copy paste functionality.
component: true
links:
  doc: https://vue-input-otp.vercel.app/
---

::component-preview
---
name: InputOTPDemo
description: An input OTP component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add input-otp
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install vue-input-otp
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/input-otp) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from '@/components/ui/input-otp'
</script>

<template>
  <InputOTP v-model="value" :maxlength="6">
    <InputOTPGroup>
      <InputOTPSlot :index="0" />
      <InputOTPSlot :index="1" />
      <InputOTPSlot :index="2" />
    </InputOTPGroup>
    <InputOTPSeparator />
    <InputOTPGroup>
      <InputOTPSlot :index="3" />
      <InputOTPSlot :index="4" />
      <InputOTPSlot :index="5" />
    </InputOTPGroup>
  </InputOTP>
</template>
```

## Examples

### Pattern

Use the `pattern` prop to define a custom pattern for the OTP input.

::component-preview
---
name: InputOTPPatternDemo
description: An input OTP component with a custom pattern.
---
::

```vue showLineNumbers {2,9}
<script setup lang="ts">
import { REGEXP_ONLY_DIGITS_AND_CHARS } from 'vue-input-otp'
// ...
</script>

<template>
  <InputOTP
    maxlength="6"
    :pattern="REGEXP_ONLY_DIGITS_AND_CHARS"
  >
    <InputOTPGroup>
      <InputOTPSlot :index="0" />
      
    </InputOTPGroup>
  </InputOTP>
</template>
```

### Separator
You can use the `<InputOTPSeparator />` component to add a separator between the input groups.

::component-preview
---
name: InputOTPSeparatorDemo
description: An input OTP component with a separator.
---
::

```vue showLineNumbers {5,17}
<script setup lang="ts">
import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from '@/components/ui/input-otp'
// ...
</script>

<template>
  <InputOTP maxlength="4">
    <InputOTPGroup>
      <InputOTPSlot :index="0" />
      <InputOTPSlot :index="1" />
    </InputOTPGroup>
    <InputOTPSeparator />
    <InputOTPGroup>
      <InputOTPSlot :index="2" />
      <InputOTPSlot :index="3" />
    </InputOTPGroup>
  </InputOTP>
</template>
```

### Controlled
You can use the `v-model` directive to control the input value.

::component-preview
---
name: InputOTPControlledDemo
description: A controlled input OTP component.
---
::

### Form

You can use the InputOTP component within a form, for example with VeeValidate.

::component-preview
---
name: InputOTPFormDemo
description: An input OTP component used within a form.
---
::

---

### Apps V4 Content Docs Components Input (`apps/v4/content/docs/components/input.md`)

---
title: Input
description: Displays a form input field or a component that looks like an input field.
component: true
---

::component-preview
---
name: InputDemo
class: '[&_input]:max-w-xs'
description: A form input component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add input
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/input) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Input } from '@/components/ui/input'
</script>

<template>
  <Input />
</template>
```

## Examples

### Default

::component-preview
---
name: InputDemo
class: '[&_input]:max-w-xs'
description: A default input component.
---
::

### File

::component-preview
---
name: InputFile
description: An input with a file upload field.
---
::

### Disabled

::component-preview
---
name: InputDisabled
class: '[&_input]:max-w-xs'
description: A disabled input component.
---
::

### With Label

::component-preview
---
name: InputWithLabel
description: An input with a label.
---
::

### With Button

::component-preview
---
name: InputWithButton
description: An input with a button.
---
::

---

### Apps V4 Content Docs Components Item (`apps/v4/content/docs/components/item.md`)

---
title: Item
description: A versatile component that you can use to display any content.
component: true
---

The `Item` component is a straightforward flex container that can house nearly any type of content. Use it to display a title, description, and actions. Group it with the `ItemGroup` component to create a list of items.

You can pretty much achieve the same result with the `div` element and some classes, but I've built this so many times that I decided to create a component for it. Now I use it all the time.

::component-preview
---
name: ItemDemo
class: '[&_.preview]:p-0'
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add item
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/item) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Item,
  ItemContent,
  ItemDescription,
  ItemFooter,
  ItemHeader,
  ItemMedia,
  ItemTitle,
} from '@/components/ui/item'
</script>

<template>
  <Item>
    <ItemHeader>Item Header</ItemHeader>
    <ItemMedia />
    <ItemContent>
      <ItemTitle>Item</ItemTitle>
      <ItemDescription>Item</ItemDescription>
    </ItemContent>
    <ItemFooter>Item Footer</ItemFooter>
  </Item>
</template>
```

## Examples

### Variants

::component-preview
---
name: ItemVariantDemo
previewClass: h-[27rem] sm:h-96
---
::

### Size

The `Item` component has different sizes for different use cases. For example, you can use the `sm` size for a compact item or the `default` size for a standard item.

::component-preview
---
name: ItemSizeDemo
---
::

### Icon

::component-preview
---
name: ItemIconDemo
---
::

### Avatar

::component-preview
---
name: ItemAvatarDemo
previewClass: h-88 sm:h-72
---
::

### Image

::component-preview
---
name: ItemImageDemo
previewClass: h-[26rem]
---
::

### Group

::component-preview
---
name: ItemGroupDemo
previewClass: h-[30rem] sm:h-80
---
::

### Header

::component-preview
---
name: ItemHeaderDemo
previewClass: h-88
---
::

### Link

To render an item as a link, use the `as-child` prop. The hover and focus states will be applied to the anchor element.

::component-preview
---
name: ItemLinkDemo
previewClass: h-80 sm:h-72
---
::

### Dropdown

::component-preview
---
name: ItemDropdownDemo
previewClass: h-88
---
::

## API Reference

### Item

The main component for displaying content with media, title, description, and actions.

| Prop       | Type                                | Default     |
| ---------- | ----------------------------------- | ----------- |
| `variant`  | `"default" \| "outline" \| "muted"` | `"default"` |
| `size`     | `"default" \| "sm"`                 | `"default"` |
| `as-child` | `boolean`                           | `false`     |

```vue
<template>
  <Item size="" variant="">
    <ItemMedia />
    <ItemContent>
      <ItemTitle>Item</ItemTitle>
      <ItemDescription>Item</ItemDescription>
    </ItemContent>
    <ItemActions />
  </Item>
</template>
```

You can use the `as-child` prop to render a custom component as the item, for example a link. The hover and focus states will be applied to the custom component.

```vue
<script setup lang="ts">
import {
  Item,
  ItemContent,
  ItemDescription,
  ItemMedia,
  ItemTitle,
} from '@/components/ui/item'
</script>

<template>
  <Item as-child>
    <a href="/dashboard">
      <ItemMedia variant="icon">
        <Home />
      </ItemMedia>
      <ItemContent>
        <ItemTitle>Dashboard</ItemTitle>
        <ItemDescription>
          Overview of your account and activity.
        </ItemDescription>
      </ItemContent>
    </a>
  </Item>
</template>
```

### ItemGroup

The `ItemGroup` component is a container that groups related items together with consistent styling.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class`     | `string` |         |

```vue
<template>
  <ItemGroup>
    <Item />
    <Item />
  </ItemGroup>
</template>
```

### ItemSeparator

The `ItemSeparator` component is a separator that separates items in the item group.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class`     | `string` |         |

```vue
<template>
  <ItemGroup>
    <Item />
    <ItemSeparator />
    <Item />
  </ItemGroup>
</template>
```

### ItemMedia

Use the `ItemMedia` component to display media content such as icons, images, or avatars.

| Prop        | Type                             | Default     |
| ----------- | -------------------------------- | ----------- |
| `variant`   | `"default" \| "icon" \| "image"` | `"default"` |
| `class`     | `string`                         |             |

```vue
<template>
  <ItemMedia variant="icon">
    <Icon />
  </ItemMedia>
</template>
```

```vue
<template>
  <ItemMedia variant="image">
    <img src="..." alt="...">
  </ItemMedia>
</template>
```

### ItemContent

The `ItemContent` component wraps the title and description of the item.

You can skip `ItemContent` if you only need a title.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class`     | `string` |         |

```vue
<template>
  <ItemContent>
    <ItemTitle>Item</ItemTitle>
    <ItemDescription>Item</ItemDescription>
  </ItemContent>
</template>
```

### ItemTitle

Use the `ItemTitle` component to display the title of the item.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class`     | `string` |         |

```vue
<template>
  <ItemTitle>Item Title</ItemTitle>
</template>
```

### ItemDescription

Use the `ItemDescription` component to display the description of the item.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class`     | `string` |         |

```vue
<template>
  <ItemDescription>Item description</ItemDescription>
</template>
```

### ItemActions

Use the `ItemActions` component to display action buttons or other interactive elements.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class`     | `string` |         |

```vue
<template>
  <ItemActions>
    <Button>Action</Button>
    <Button>Action</Button>
  </ItemActions>
</template>
```

### ItemHeader

Use the `ItemHeader` component to display a header in the item.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class`     | `string` |         |

```vue
<template>
  <ItemHeader>Item Header</ItemHeader>
</template>
```

### ItemFooter

Use the `ItemFooter` component to display a footer in the item.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class`     | `string` |         |

```vue
<template>
  <ItemFooter>Item Footer</ItemFooter>
</template>
```

---

### Apps V4 Content Docs Components Kbd (`apps/v4/content/docs/components/kbd.md`)

---
title: Kbd
description: Used to display textual user input from keyboard.
component: true
---

::component-preview
---
name: KbdDemo
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add kbd
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/kbd) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue
<script setup lang="ts">
import { Kbd } from '@/components/ui/kbd'
</script>

<template>
  <Kbd>Ctrl</Kbd>
</template>
```

## Examples

### Group

Use the `KbdGroup` component to group keyboard keys together.

::component-preview
---
name: KbdGroupDemo
---
::

### Button

Use the `Kbd` component inside a `Button` component to display a keyboard key inside a button.

::component-preview
---
name: KbdWithButton
---
::

### Tooltip

You can use the `Kbd` component inside a `Tooltip` component to display a tooltip with a keyboard key.

::component-preview
---
name: KbdWithTooltip
---
::

### Input Group

You can use the `Kbd` component inside a `InputGroupAddon` component to display a keyboard key inside an input group.

::component-preview
---
name: KbdWithInputGroup
---
::

## API Reference

### Kbd

Use the `Kbd` component to display a keyboard key.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` | ``      |

```vue
<Kbd>
Ctrl
</Kbd>
```

### KbdGroup

Use the `KbdGroup` component to group `Kbd` components together.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` | ``      |

```vue
<KbdGroup>
  <Kbd>Ctrl</Kbd>
  <Kbd>B</Kbd>
</KbdGroup>
```

---

### Apps V4 Content Docs Components Label (`apps/v4/content/docs/components/label.md`)

---
title: Label
description: Renders an accessible label associated with controls.
component: true
links:
  doc: https://reka-ui.com/docs/components/label
  api: https://reka-ui.com/docs/components/label#api-reference
---

::component-preview
---
name: LabelDemo
description: A label
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add label
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/label) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Label } from '@/components/ui/label'
</script>

<template>
  <Label for="email">Your email address</Label>
</template>
```

---

### Apps V4 Content Docs Components Marker (`apps/v4/content/docs/components/marker.md`)

---
title: Marker
description: Displays an inline status, system note, bordered row, or labeled separator in a conversation.
component: true 
---

::component-preview
---
name: MarkerDemo
class: style-luma
previewClass: h-auto theme-blue
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add marker
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/bases/reka/ui/marker) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Marker, MarkerContent, MarkerIcon, } from '@/components/ui/marker'
</script>

<template>
  <Marker>
    <MarkerIcon>
      <CheckIcon />
    </MarkerIcon>
    <MarkerContent>Explored 4 files</MarkerContent>
  </Marker>
</template>
```


## Composition

Use the following composition to build a marker:

```text
Marker
├── MarkerIcon
└── MarkerContent
```

## Features

- Inline marker, bordered row, and labeled separator variants
- Decorative icon slot that is hidden from assistive tech
- Polymorphic root via `render` for link and button markers
- Pairs with the [`shimmer`](/docs/utilities/shimmer) utility for streaming status text
- Customizable styling through the `class` prop on every part

## Examples

### Variants

Use `variant` to switch between an inline marker, bordered row, and labeled separator.

::component-preview
---
name: MarkerVariantsDemo
class: style-luma
previewClass: h-auto theme-blue
---
::

| Variant     | Description                                          |
| ----------- | ---------------------------------------------------- |
| `default`   | An inline marker for status, notes, and actions.     |
| `border`    | A default marker with a bottom border under the row. |
| `separator` | A centered label with divider lines on each side.    |

### Status

Set `role="status"` and include a [`Spinner`](/docs/components/spinner) for streaming or in-progress markers so updates are announced.

::component-preview
---
name: MarkerStatusDemo
class: style-luma
previewClass: h-auto theme-blue
---
::


### Shimmer

Add the [`shimmer`](/docs/utils/shimmer) utility class to `MarkerContent` for an animated streaming-text effect. The utility ships with the `shadcn` package — see the shimmer docs for installation.



::component-preview
---
name: MarkerShimmerDemo
class: style-luma
previewClass: h-auto theme-blue
---
::

### Separator

Use the `separator` variant for labeled dividers, such as dates or section breaks, in a conversation.

::component-preview
---
name: MarkerSeparatorDemo
class: style-luma
previewClass: h-auto theme-blue
---
::

### Border

Use the `border` variant for status rows that should keep the default marker alignment while separating the next row.

::component-preview
---
name: MarkerBorderDemo
class: style-luma
previewClass: h-auto theme-blue
---
::

### With Icon

Use `MarkerIcon` to render an icon alongside the content. Use `flex-col` to stack the icon above the content.


::component-preview
---
name: MarkerIconDemo
class: style-luma
previewClass: h-auto theme-blue
---
::

### Links and Buttons

Turn a marker into a link or button with the `render` prop on `Marker`.


::component-preview
---
name: MarkerLinkButtonDemo
class: style-luma
previewClass: h-auto theme-blue
---
::

```tsx showLineNumbers
import { Marker, MarkerContent } from "@/components/ui/marker"

<template>
  <Marker as-child>
    <a href="#links-and-buttons">
      <MarkerIcon>
        <GitBranchIcon />
      </MarkerIcon>
      <MarkerContent>View the pull request</MarkerContent>
    </a>
  </Marker>
</template>
```

## Accessibility

`Marker` is presentational by default. The correct semantics depend on how you use it, so choose the role based on intent rather than relying on a single default.

### Status and Progress

For streaming or progress markers such as "Thinking..." or a running tool, set `role="status"` so assistive tech announces the update as it appears. `Marker` forwards `role` to the underlying element.

```tsx showLineNumbers
<Marker role="status">
  <MarkerIcon>
    <Spinner />
  </MarkerIcon>
  <MarkerContent>Compacting conversation</MarkerContent>
</Marker>
```

### Labeled Separators

A separator that carries text, such as a date or a section label, needs no role. The divider lines are decorative CSS pseudo-elements, and the text is announced as ordinary content.

```tsx showLineNumbers
<Marker variant="separator">
  <MarkerContent>Today</MarkerContent>
</Marker>
```

::callout
  **Note:** Do not add `role="separator"` to a labeled divider. A separator
  takes its accessible name from `aria-label`, not from its text, and its
  contents are treated as presentational, so the visible label would not be
  announced. Reserve `role="separator"` for a divider with no meaningful text.
::

### Bordered Markers

A bordered marker keeps the same semantics as the default marker. The bottom border is decorative, so choose `role="status"`, `render`, or no role based on the marker's purpose.

```tsx showLineNumbers
<Marker variant="border">
  <MarkerIcon>
    <FileTextIcon />
  </MarkerIcon>
  <MarkerContent>Opened implementation notes</MarkerContent>
</Marker>
```

### Decorative Icons

`MarkerIcon` is decorative and hidden from assistive tech with `aria-hidden`, so the adjacent `MarkerContent` carries the meaning. For an icon-only marker, provide an `aria-label` or visible text so it is not announced as empty.

```tsx showLineNumbers
<Marker aria-label="Synced">
  <MarkerIcon>
    <CheckIcon />
  </MarkerIcon>
</Marker>
```

### Interactive Markers

When a marker links or triggers an action, render it as a real `<button>` or `<a>` with the `render` prop so it is focusable and exposes the correct role. The accessible name comes from the marker text.

```tsx showLineNumbers
<Marker as-child>
  <a href="/files" >
    <MarkerIcon>
      <FileTextIcon />
    </MarkerIcon>
    <MarkerContent>Explored 4 files</MarkerContent>
  </a>
</Marker>
```

## API Reference

### Marker

The root marker element. The file also exports `markerVariants` for composing the marker styles into custom components.

| Prop        | Type                                   | Default     | Description                                      |
| ----------- | -------------------------------------- | ----------- | ------------------------------------------------ |
| `variant`   | `"default" \| "border" \| "separator"` | `"default"` | The marker layout.                               |
| `render`    | `ReactElement \| function`             | -           | Render as a different element, such as a link.   |
| `class` | `string`                               | -           | Additional classes to apply to the root element. |

### MarkerIcon

A decorative icon slot. Hidden from assistive tech with `aria-hidden`.

| Prop        | Type     | Default | Description                                   |
| ----------- | -------- | ------- | --------------------------------------------- |
| `class` | `string` | -       | Additional classes to apply to the icon slot. |

### MarkerContent

The marker text content.

| Prop        | Type     | Default | Description                                      |
| ----------- | -------- | ------- | ------------------------------------------------ |
| `class` | `string` | -       | Additional classes to apply to the content slot. |

---

### Apps V4 Content Docs Components Menubar (`apps/v4/content/docs/components/menubar.md`)

---
title: Menubar
description: A visually persistent menu common in desktop applications that provides quick access to a consistent set of commands.
component: true
links:
  doc: https://reka-ui.com/docs/components/menubar
  api: https://reka-ui.com/docs/components/menubar#api-reference
---

::component-preview
---
name: MenubarDemo
description: A menubar component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add menubar
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/menubar) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Menubar,
  MenubarContent,
  MenubarItem,
  MenubarMenu,
  MenubarSeparator,
  MenubarShortcut,
  MenubarTrigger,
} from '@/components/ui/menubar'
</script>

<template>
  <Menubar>
    <MenubarMenu>
      <MenubarTrigger>File</MenubarTrigger>
      <MenubarContent>
        <MenubarItem>
          New Tab <MenubarShortcut>⌘T</MenubarShortcut>
        </MenubarItem>
        <MenubarItem>New Window</MenubarItem>
        <MenubarSeparator />
        <MenubarItem>Share</MenubarItem>
        <MenubarSeparator />
        <MenubarItem>Print</MenubarItem>
      </MenubarContent>
    </MenubarMenu>
  </Menubar>
</template>
```

---

### Apps V4 Content Docs Components Message Scroller (`apps/v4/content/docs/components/message-scroller.md`)

---
title: Message Scroller
description: A scroll container for chat transcripts that anchors turns, follows streamed replies, restores prepended history, and jumps to messages.
component: true
---

::component-preview
---
name: MessageScrollerDemo
class: "style-luma rounded-[34px] sm:rounded-4xl"
previewClass: h-auto theme-green bg-surface dark:bg-background p-4 min-[480px]:p-8 min-[560px]:p-10 sm:px-10 sm:py-16
---
::

## MessageScroller

A great streaming chat scroller has to juggle a lot at once: pin to the live edge while a reply streams, but never fight a reader who scrolls up; anchor each new turn near the top with a peek of the previous exchange; preserve position when older history loads above; and expose commands to jump anywhere in the thread. `MessageScroller` owns those hard parts so your message list doesn't have to.

It does **not** own your messages, AI state, transport, or model — it is a headless scroll container you compose around your own rows.

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add message-scroller
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/bases/reka/ui/message-scroller) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  MessageScroller,
  MessageScrollerButton,
  MessageScrollerContent,
  MessageScrollerItem,
  MessageScrollerProvider,
  MessageScrollerViewport,
} from '@/components/ui/message-scroller'
</script>

<template>
  <MessageScrollerProvider auto-scroll default-scroll-position="last-anchor">
    <MessageScroller>
      <MessageScrollerViewport>
        <MessageScrollerContent>
          <MessageScrollerItem
            v-for="message in messages"
            :key="message.id"
            :message-id="message.id"
            :scroll-anchor="message.role === 'user'"
          >
            
          </MessageScrollerItem>
        </MessageScrollerContent>
      </MessageScrollerViewport>
      <MessageScrollerButton direction="end" />
    </MessageScroller>
  </MessageScrollerProvider>
</template>
```

The provider must have a constrained height (or a height-bounded parent) so the viewport can scroll.

## Composition

```text
MessageScrollerProvider
└── MessageScroller
    ├── MessageScrollerViewport
    │   └── MessageScrollerContent
    │       └── MessageScrollerItem
    └── MessageScrollerButton
```

## Core Concepts

### Anchoring Turns

A turn is the part of the conversation that starts a new exchange — usually the user's message and the assistant reply that follows. An *anchor* is the row the viewport should treat as the start of that turn. Mark that row with `scrollAnchor`. When a new anchor is appended, the viewport moves it near the top and keeps a peek of the previous item above it, so the new turn does not feel detached from its context.

```vue
<MessageScrollerItem
  :message-id="message.id"
  :scroll-anchor="message.role === 'user'"
>
  
</MessageScrollerItem>
```

Scroll anchors are not tied to message role. You can turn any row into an anchor: a user message, a system marker, a handoff event, or anything else that starts a meaningful turn.

::component-preview
---
name: MessageScrollerAnchoringDemo
class: "style-luma rounded-[34px] sm:rounded-4xl"
previewClass: h-auto theme-green bg-surface dark:bg-background p-4 min-[480px]:p-8 min-[560px]:p-10 sm:px-10 sm:py-16
---
::

### Group Chat

In a group chat, the turn boundary is often the message that asks the model to respond, or a marker like "Marcus joined the chat". Typing indicators and history controls usually should not anchor. Because anchoring is role-independent, you can anchor a marker just as easily as a message.

::component-preview
---
name: MessageScrollerGroupChatDemo
class: "style-luma rounded-[34px] sm:rounded-4xl"
previewClass: h-auto theme-green bg-surface dark:bg-background p-4 min-[480px]:p-8 min-[560px]:p-10 sm:px-10 sm:py-16
---
::

### Keeping Context Visible

When a new turn starts, it should still feel like part of the same continuous thread. `scrollPreviousItemPeek` keeps a slice of the previous item visible above the anchor, so the reader keeps their context instead of feeling like the conversation restarted on a blank page.

::component-preview
---
name: MessageScrollerPreviousContextDemo
class: "style-luma rounded-[34px] sm:rounded-4xl"
previewClass: h-auto theme-green bg-surface dark:bg-background p-4 min-[480px]:p-8 min-[560px]:p-10 sm:px-10 sm:py-16
---
::

### Following the Live Edge

When the reader is at the live edge, `autoScroll` keeps streamed replies in view as they grow. Scrolling away from the live edge — by wheel, touch, keyboard, or dragging the scrollbar — releases the view, so new chunks arrive without moving the reader. `autoScroll` composes with turn anchoring: when a new turn anchors near the top, the view stays put while the reply streams into the room below it.

::component-preview
---
name: MessageScrollerStreamingDemo
class: "style-luma rounded-[34px] sm:rounded-4xl"
previewClass: h-auto theme-green bg-surface dark:bg-background p-4 min-[480px]:p-8 min-[560px]:p-10 sm:px-10 sm:py-16
---
::

### Opening Saved Threads

Reopening a saved thread at the absolute end often drops the reader in without enough context. A better default is `"last-anchor"`: show the last meaningful turn, like the user's latest message, with the reply below it.

::component-preview
---
name: MessageScrollerOpeningPositionDemo
class: "style-luma rounded-[34px] sm:rounded-4xl"
previewClass: h-auto theme-green bg-surface dark:bg-background p-4 min-[480px]:p-8 min-[560px]:p-10 sm:px-10 sm:py-16
---
::

### Loading Earlier Messages

Loading earlier messages should not move the conversation the reader is already looking at. When older rows are prepended above the current transcript, `MessageScrollerViewport` preserves the visible row so the reader stays in the same place while history loads above them. This is enabled by default through `preserveScrollOnPrepend`.

::component-preview
---
name: MessageScrollerLoadHistoryDemo
class: "style-luma rounded-[34px] sm:rounded-4xl"
previewClass: h-auto theme-green bg-surface dark:bg-background p-4 min-[480px]:p-8 min-[560px]:p-10 sm:px-10 sm:py-16
---
::

### Animating New Messages

A common chat pattern is to animate the user's message when it is sent, then let the assistant reply stream into a regular row below it. Keep `messageId` and `scrollAnchor` on the animated item and use transform and opacity for the entrance — avoid animating height, margin, or padding, which can fight the scroller's positioning.

::component-preview
---
name: MessageScrollerAnimationDemo
class: "style-luma rounded-[34px] sm:rounded-4xl"
previewClass: h-auto theme-green bg-surface dark:bg-background p-4 min-[480px]:p-8 min-[560px]:p-10 sm:px-10 sm:py-16
---
::

### Jumping to Messages

Search results, permalinks, outline items, and toolbar buttons often need to drive the transcript from outside the message list. Use `useMessageScroller` for those controls — the composables read from `MessageScrollerProvider`, so they work in any component inside the provider.

```vue
<script setup lang="ts">
import { useMessageScroller } from '@/components/ui/message-scroller'

const { scrollToMessage, scrollToEnd, scrollToStart } = useMessageScroller()
</script>
```

::component-preview
---
name: MessageScrollerCommandsDemo
class: "style-luma rounded-[34px] sm:rounded-4xl"
previewClass: h-auto theme-green bg-surface dark:bg-background p-4 min-[480px]:p-8 min-[560px]:p-10 sm:px-10 sm:py-16
---
::

### Tracking the Reader's Position

Use `useMessageScrollerVisibility` to track the reader's position — a table-of-contents or jump menu that highlights the current anchored turn. `currentAnchorId` answers "where am I" and stays set after that anchor scrolls above the viewport; `visibleMessageIds` answers "what is on screen", in document order.

::component-preview
---
name: MessageScrollerVisibilityDemo
class: "style-luma rounded-[34px] sm:rounded-4xl"
previewClass: h-auto theme-green bg-surface dark:bg-background p-4 min-[480px]:p-8 min-[560px]:p-10 sm:px-10 sm:py-16
---
::

### Reading Scroll State

Use `useMessageScrollerScrollable` when you need scroll state in JavaScript, such as a status indicator or a custom "jump to latest" control. It reports which edges the viewport can still scroll toward.

::component-preview
---
name: MessageScrollerScrollableDemo
class: "style-luma rounded-[34px] sm:rounded-4xl"
previewClass: h-auto theme-green bg-surface dark:bg-background p-4 min-[480px]:p-8 min-[560px]:p-10 sm:px-10 sm:py-16
---
::

## API Reference

### MessageScrollerProvider

Owns the scroll state and behavior. Provide it via `provide`/`inject` and expose the scroll composables to descendants.

| Prop                     | Type                                  | Default        | Description                                                            |
| ------------------------ | ------------------------------------- | -------------- | -------------------------------------------------------------------- |
| `autoScroll`             | `boolean`                             | `false`        | Follow the live edge while the reader is pinned to the bottom.        |
| `defaultScrollPosition`  | `'start' \| 'end' \| 'last-anchor'`   | `'end'`        | Opening position for the transcript.                                  |
| `scrollEdgeThreshold`    | `number`                              | `8`            | Distance in px from an edge before it is considered scrollable.       |
| `scrollPreviousItemPeek` | `number`                              | `64`           | Amount in px of the previous turn kept visible when anchoring.        |
| `scrollMargin`           | `number`                              | `0`            | Extra offset in px applied when scrolling to an element.              |

### MessageScrollerViewport

| Prop                       | Type      | Default | Description                                             |
| -------------------------- | --------- | ------- | ------------------------------------------------------- |
| `preserveScrollOnPrepend`  | `boolean` | `true`  | Keep the current view when messages are added above.    |

Rendered as a `role="region"`, `aria-label="Messages"`, focusable (`tabindex="0"`) native scroll container.

### MessageScrollerItem

| Prop           | Type      | Default | Description                                      |
| -------------- | --------- | ------- | ------------------------------------------------ |
| `messageId`    | `string`  | —       | Stable id used for anchoring, visibility, jumps. |
| `scrollAnchor` | `boolean` | `false` | Marks this row as the start of a turn.           |

### MessageScrollerButton

| Prop        | Type                  | Default   | Description                              |
| ----------- | --------------------- | --------- | ---------------------------------------- |
| `direction` | `'start' \| 'end'`    | `'end'`   | Direction the button scrolls toward.     |
| `behavior`  | `ScrollBehavior`      | `'smooth'`| Scroll behavior for the jump.            |
| `variant`   | `ButtonVariants`      | `'secondary'` | Button variant.                      |
| `size`      | `ButtonVariants`      | `'icon-sm'`   | Button size.                         |

Exposes `data-active` for styling and becomes `inert` with `tabindex="-1"` when there is nothing to scroll toward.

### Composables

#### useMessageScroller()

```ts
const { scrollToMessage, scrollToEnd, scrollToStart } = useMessageScroller()
```

- `scrollToMessage(id, options?)` — scroll to the item with the matching `messageId`. Returns `true` if handled (queued if the item is not mounted yet), `false` if the id is missing after rows have mounted.
- `scrollToEnd(options?)` / `scrollToStart(options?)` — scroll to the live edge or the top.

#### useMessageScrollerVisibility()

```ts
const visibility = useMessageScrollerVisibility()
// visibility.value.currentAnchorId, visibility.value.visibleMessageIds
```

Tracking only runs while something subscribes, and rows need a `messageId` to participate.

#### useMessageScrollerScrollable()

```ts
const scrollable = useMessageScrollerScrollable()
// scrollable.value.start, scrollable.value.end
```

Reports which edges the viewport can still scroll toward. For styling the scroller itself, prefer the `data-scrollable` attribute.

---

### Apps V4 Content Docs Components Message (`apps/v4/content/docs/components/message.md`)

---
title: Message
description: Displays a message in a conversation, with optional avatar, header, footer, and alignment.
component: true
---

::component-preview
---
name: MessageDemo
previewClass: h-auto theme-blue
---
::

The `Message` component lays out a single message in a conversation. It handles the avatar, alignment, header, and footer around the message surface.

For AI apps, you can render reasoning steps, tool calls and assistant messages using the `Message` component.

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add message
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/message) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Bubble, BubbleContent } from "@/components/ui/bubble"
import { Message, MessageAvatar, MessageContent } from "@/components/ui/message"
</script>

<template>
  <Message>
    <MessageAvatar>
      <Avatar>
        <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
        <AvatarFallback>CN</AvatarFallback>
      </Avatar>
    </MessageAvatar>
    <MessageContent>
      <Bubble>
        <BubbleContent>How can I help you today?</BubbleContent>
      </Bubble>
    </MessageContent>
  </Message>
</template>
```


**Note:** `Message` owns the row layout—avatar, alignment, header, and footer.
Render the visible message surface inside it with
[`Bubble`](/docs/components/bubble).

## Composition

Use the following composition to build a message:

```text
Message
├── MessageAvatar
└── MessageContent
    ├── MessageHeader
    ├── Bubble
    └── MessageFooter
```

Use `MessageGroup` to stack consecutive messages from the same sender:

```text
MessageGroup
├── Message
└── Message
```

## Features

- Start and end alignment for sender and receiver rows via the `align` prop
- Avatar slot that anchors to the bottom of the message and stays clear of the footer
- Header and footer slots for sender names, status, and message actions
- Footer follows the message side; actions stay aligned on `align="end"` rows
- Group wrapper for stacking consecutive messages from the same sender
- Polymorphic wrappers via `as` or `as-child`
- Customizable styling through the `class` prop on every part

## Examples

### Avatar

Use `MessageAvatar` to render an avatar next to the message. Set `align="end"` on the message to align the avatar to the end of the message.

::component-preview
---
name: MessageAvatarDemo
previewClass: h-auto theme-blue
---
::

| align   | Description                                         |
| ------- | --------------------------------------------------- |
| `start` | Align the message to the start of the conversation. |
| `end`   | Align the message to the end of the conversation.   |

### Group

Use `MessageGroup` to stack consecutive messages from the same sender. Render an empty `MessageAvatar` on the earlier messages to keep them aligned with the avatar on the last one.

::component-preview
---
name: MessageGroupDemo
previewClass: h-auto theme-blue
---
::

### Header and Footer

Use `MessageHeader` for a sender name and `MessageFooter` for metadata such as a delivery or read status.

::component-preview
---
name: MessageHeaderAndFooterDemo
previewClass: h-auto theme-blue
---
::

### Actions

Place message-level actions in `MessageFooter`, such as copy, retry, or feedback buttons.

::component-preview
---
name: MessageActionsDemo
previewClass: h-auto theme-blue
---
::

## Accessibility

`Message` is a presentational layout wrapper. Accessibility comes from the content you place inside it.

### Label icon-only actions

Action buttons in `MessageFooter` are usually icon-only, so give each one an `aria-label`.

```vue showLineNumbers
<MessageFooter>
  <Button variant="ghost" size="icon" aria-label="Copy">
    <CopyIcon />
  </Button>
</MessageFooter>
```

### Status updates

For in-progress messages, use a [`Marker`](/docs/components/marker) with `role="status"` so assistive tech announces the update as it appears.

```vue showLineNumbers
<Message>
  <Marker role="status">
    <MarkerIcon>
      <Spinner />
    </MarkerIcon>
    <MarkerContent>Checking the logs...</MarkerContent>
  </Marker>
</Message>
```

## API Reference

All Message parts render a `<div>` by default. Use `as` to choose another element, or `as-child` to merge the component's attributes and styles onto the single element or component in its default slot.

### Message

The message row wrapper.

| Prop       | Type                 | Default   | Description                                                  |
| ---------- | -------------------- | --------- | ------------------------------------------------------------ |
| `align`    | `"start" \| "end"` | `"start"` | The alignment of the message in the conversation.            |
| `as`       | `string \| Component` | `"div"`   | The element or component to render.                          |
| `as-child` | `boolean`            | `false`   | Render the default slot as the root and merge props onto it. |
| `class`    | `string`             | -         | Additional classes to apply to the row.                      |

### MessageGroup

Groups consecutive messages from the same sender.

| Prop       | Type                 | Default | Description                                                  |
| ---------- | -------------------- | ------- | ------------------------------------------------------------ |
| `as`       | `string \| Component` | `"div"` | The element or component to render.                          |
| `as-child` | `boolean`            | `false` | Render the default slot as the root and merge props onto it. |
| `class`    | `string`             | -       | Additional classes to apply to the group root.               |

### MessageAvatar

The avatar slot, aligned to the bottom of the message. When the message has a `MessageFooter`, the avatar shifts up to stay aligned with the message surface instead of the footer.

| Prop       | Type                 | Default | Description                                                  |
| ---------- | -------------------- | ------- | ------------------------------------------------------------ |
| `as`       | `string \| Component` | `"div"` | The element or component to render.                          |
| `as-child` | `boolean`            | `false` | Render the default slot as the root and merge props onto it. |
| `class`    | `string`             | -       | Additional classes to apply to the avatar wrapper.           |

### MessageContent

Wraps the header, message surface, and footer.

| Prop       | Type                 | Default | Description                                                  |
| ---------- | -------------------- | ------- | ------------------------------------------------------------ |
| `as`       | `string \| Component` | `"div"` | The element or component to render.                          |
| `as-child` | `boolean`            | `false` | Render the default slot as the root and merge props onto it. |
| `class`    | `string`             | -       | Additional classes to apply to the content wrapper.          |

### MessageHeader

Displays content above the message, such as a sender name. Aligns to the message side.

| Prop       | Type                 | Default | Description                                                  |
| ---------- | -------------------- | ------- | ------------------------------------------------------------ |
| `as`       | `string \| Component` | `"div"` | The element or component to render.                          |
| `as-child` | `boolean`            | `false` | Render the default slot as the root and merge props onto it. |
| `class`    | `string`             | -       | Additional classes to apply to the header.                   |

### MessageFooter

Displays content below the message, such as status or actions. Aligns to the message side.

| Prop       | Type                 | Default | Description                                                  |
| ---------- | -------------------- | ------- | ------------------------------------------------------------ |
| `as`       | `string \| Component` | `"div"` | The element or component to render.                          |
| `as-child` | `boolean`            | `false` | Render the default slot as the root and merge props onto it. |
| `class`    | `string`             | -       | Additional classes to apply to the footer.                   |

---

### Apps V4 Content Docs Components Native Select (`apps/v4/content/docs/components/native-select.md`)

---
title: Native Select
description: A styled native HTML select element with consistent design system integration.
component: true
---

::callout
---
icon: true
---
For a styled select component, see the [Select](/docs/components/select) component.
::

::component-preview
---
name: NativeSelectDemo
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add native-select
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/native-select) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  NativeSelect,
  NativeSelectOptGroup,
  NativeSelectOption,
} from '@/components/ui/native-select'
</script>

<template>
  <NativeSelect>
    <NativeSelectOption value="">
      Select a fruit
    </NativeSelectOption>
    <NativeSelectOption value="apple">
      Apple
    </NativeSelectOption>
    <NativeSelectOption value="banana">
      Banana
    </NativeSelectOption>
    <NativeSelectOption value="blueberry">
      Blueberry
    </NativeSelectOption>
    <NativeSelectOption value="grapes" disabled>
      Grapes
    </NativeSelectOption>
    <NativeSelectOption value="pineapple">
      Pineapple
    </NativeSelectOption>
  </NativeSelect>
</template>
```

## Examples

### With Groups

Organize options using `NativeSelectOptGroup` for better categorization.

::component-preview
---
name: NativeSelectGroupsDemo
---
::

```vue showLineNumbers
<template>
  <NativeSelect>
    <NativeSelectOption value="">
      Select a food
    </NativeSelectOption>
    <NativeSelectOptGroup label="Fruits">
      <NativeSelectOption value="apple">
        Apple
      </NativeSelectOption>
      <NativeSelectOption value="banana">
        Banana
      </NativeSelectOption>
      <NativeSelectOption value="blueberry">
        Blueberry
      </NativeSelectOption>
    </NativeSelectOptGroup>
    <NativeSelectOptGroup label="Vegetables">
      <NativeSelectOption value="carrot">
        Carrot
      </NativeSelectOption>
      <NativeSelectOption value="broccoli">
        Broccoli
      </NativeSelectOption>
      <NativeSelectOption value="spinach">
        Spinach
      </NativeSelectOption>
    </NativeSelectOptGroup>
  </NativeSelect>
</template>
```

### Disabled State

Disable individual options or the entire select component.

::component-preview
---
name: NativeSelectDisabledDemo
---
::

### Invalid State

Show validation errors with the `aria-invalid` attribute and error styling.

::component-preview
---
name: NativeSelectInvalidDemo
---
::

```vue showLineNumbers
<template>
  <NativeSelect aria-invalid="true">
    <NativeSelectOption value="">
      Select a country
    </NativeSelectOption>
    <NativeSelectOption value="us">
      United States
    </NativeSelectOption>
    <NativeSelectOption value="uk">
      United Kingdom
    </NativeSelectOption>
    <NativeSelectOption value="ca">
      Canada
    </NativeSelectOption>
  </NativeSelect>
</template>
```

### Form Integration

Use with form libraries like VeeValidate for controlled components.

::component-preview
---
name: NativeSelectFormDemo
---
::



## Native Select vs Select

- Use `NativeSelect` when you need native browser behavior, better performance, or mobile-optimized dropdowns.
- Use `Select` when you need custom styling, animations, or complex interactions.

The `NativeSelect` component provides native HTML select functionality with consistent styling that matches your design system.

## Accessibility

- The component maintains all native HTML select accessibility features.
- Screen readers can navigate through options using arrow keys.
- The chevron icon is marked as `aria-hidden="true"` to avoid duplication.
- Use `aria-label` or `aria-labelledby` for additional context when needed.

```vue showLineNumbers
<template>
  <NativeSelect aria-label="Choose your preferred language">
    <NativeSelectOption value="en">
      English
    </NativeSelectOption>
    <NativeSelectOption value="es">
      Spanish
    </NativeSelectOption>
    <NativeSelectOption value="fr">
      French
    </NativeSelectOption>
  </NativeSelect>
</template>
```

## API Reference

### NativeSelect

The main select component that wraps the native HTML select element.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class` | `string` |         |

All other props are passed through to the underlying `<select>` element.

```vue
<NativeSelect>
  <NativeSelectOption value="option1">Option 1</NativeSelectOption>
  <NativeSelectOption value="option2">Option 2</NativeSelectOption>
</NativeSelect>
```

### NativeSelectOption

Represents an individual option within the select.

| Prop        | Type      | Default |
| ----------- | --------- | ------- |
| `value`     | `string`  |         |
| `disabled`  | `boolean` | `false` |
| `class` | `string`  |         |

All other props are passed through to the underlying `<option>` element.

```vue
<template>
  <NativeSelectOption value="apple">
    Apple
  </NativeSelectOption>
  <NativeSelectOption value="banana" disabled>
    Banana
  </NativeSelectOption>
</template>
```

### NativeSelectOptGroup

Groups related options together for better organization.

| Prop        | Type      | Default |
| ----------- | --------- | ------- |
| `label`     | `string`  |         |
| `disabled`  | `boolean` | `false` |
| `class` | `string`  |         |

All other props are passed through to the underlying `<optgroup>` element.

```vue
<template>
  <NativeSelectOptGroup label="Fruits">
    <NativeSelectOption value="apple">
      Apple
    </NativeSelectOption>
    <NativeSelectOption value="banana">
      Banana
    </NativeSelectOption>
  </NativeSelectOptGroup>
</template>
```

---

### Apps V4 Content Docs Components Navigation Menu (`apps/v4/content/docs/components/navigation-menu.md`)

---
title: Navigation Menu
description: A collection of links for navigating websites.
component: true
links:
  doc: https://reka-ui.com/docs/components/navigation-menu
  api: https://reka-ui.com/docs/components/navigation-menu#api-reference
---

::component-preview
---
name: NavigationMenuDemo
description: A navigation menu component.
class: '[&_.preview]:!items-start [&_.preview]:p-4 [&_.preview]:pt-8 md:[&_.preview]:pt-16'
previewClass: h-96
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add navigation-menu
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/navigation-menu) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuIndicator,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
  NavigationMenuViewport,
} from '@/components/ui/navigation-menu'
</script>

<template>
  <NavigationMenu>
    <NavigationMenuList>
      <NavigationMenuItem>
        <NavigationMenuTrigger>Item One</NavigationMenuTrigger>
        <NavigationMenuContent>
          <NavigationMenuLink>Link</NavigationMenuLink>
        </NavigationMenuContent>
      </NavigationMenuItem>
    </NavigationMenuList>
  </NavigationMenu>
</template>
```

---

### Apps V4 Content Docs Components Number Field (`apps/v4/content/docs/components/number-field.md`)

---
title: Number Field
description: A number field allows a user to enter a number and increment or decrement the value using stepper buttons.
component: true
links:
  doc: https://reka-ui.com/docs/components/number-field
  api: https://reka-ui.com/docs/components/number-field#api-reference
---

::component-preview
---
name: NumberFieldDemo
description: An default Number Field
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add number-field
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/number-field) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

### Disabled
::component-preview
---
name: NumberFieldDisabledDemo
description: Disabled Number Field
---
::

### Decimal
::component-preview
---
name: NumberFieldDecimalDemo
description: Number Field with decimal support
---
::

### Percentage
::component-preview
---
name: NumberFieldPercentageDemo
description: Percentage Number Field
---
::

### Currency
::component-preview
---
name: NumberFieldCurrencyDemo
description: Currency Number Field
---
::

### Form
::component-preview
---
name: NumberFieldFormDemo
description: Number Field inside a form
---
::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  NumberField,
  NumberFieldContent,
  NumberFieldDecrement,
  NumberFieldIncrement,
  NumberFieldInput,
} from '@/components/ui/number-field'
</script>

<template>
  <NumberField :default-value="18" :min="0">
    <NumberFieldContent>
      <NumberFieldDecrement />
      <NumberFieldInput />
      <NumberFieldIncrement />
    </NumberFieldContent>
  </NumberField>
</template>

```

---

### Apps V4 Content Docs Components Pagination (`apps/v4/content/docs/components/pagination.md`)

---
title: Pagination
description: Pagination with page navigation, next and previous links.
component: true
links:
  doc: https://reka-ui.com/docs/components/pagination
  api: https://reka-ui.com/docs/components/pagination#api-reference
---

::component-preview
---
name: PaginationDemo
description: A pagination component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add pagination
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/pagination) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/components/ui/pagination'
</script>

<template>
  <Pagination>
    <PaginationContent>
      <PaginationItem>
        <PaginationPrevious href="#" />
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="#">
          1
        </PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="#" is-active>
          2
        </PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="#">
          3
        </PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationEllipsis />
      </PaginationItem>
      <PaginationItem>
        <PaginationNext href="#" />
      </PaginationItem>
    </PaginationContent>
  </Pagination>
</template>
```

---

### Apps V4 Content Docs Components Pin Input (`apps/v4/content/docs/components/pin-input.md`)

---
title: Pin Input
description: Accessible pin input component with copy paste functionality.
component: true
links:
  doc: https://reka-ui.com/docs/components/pin-input
  api: https://reka-ui.com/docs/components/pin-input#api-reference
---

<Callout title="The pin input component has been deprecated." class="mt-0">

  See the [input otp](/docs/components/input-otp) documentation for more information.

</Callout>

If you're looking for the old pin input component, see the [old docs](https://v3.shadcn-vue.com/docs/components/pin-input) for more information.

---

### Apps V4 Content Docs Components Popover (`apps/v4/content/docs/components/popover.md`)

---
title: Popover
description: Displays rich content in a portal, triggered by a button.
component: true
links:
  doc: https://reka-ui.com/docs/components/popover
  api: https://reka-ui.com/docs/components/popover#api-reference
---

::component-preview
---
name: PopoverDemo
description: A popover component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add popover
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/popover) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from '@/components/ui/popover'
</script>

<template>
  <Popover>
    <PopoverTrigger>Open</PopoverTrigger>
    <PopoverContent>Place content for the popover here.</PopoverContent>
  </Popover>
</template>
```

---

### Apps V4 Content Docs Components Progress (`apps/v4/content/docs/components/progress.md`)

---
title: Progress
description: Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.
component: true
links:
  doc: https://reka-ui.com/docs/components/progress
  api: https://reka-ui.com/docs/components/progress#api-reference
---

::component-preview
---
name: ProgressDemo
description: A progress component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add progress
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/progress) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Progress } from '@/components/ui/progress'
</script>

<template>
  <Progress :model-value="33" />
</template>
```

---

### Apps V4 Content Docs Components Questionnaire (`apps/v4/content/docs/components/questionnaire.md`)

---
title: Questionnaire
description: A multi-step questionnaire with single-choice, multiple-choice, freeform, and skippable questions.
component: true
new: true
---

::component-preview
---
name: QuestionnaireDemo
class: style-nova
align: end
previewClass: min-h-[560px] p-4 sm:p-8
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add questionnaire
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/bases/reka/ui/questionnaire) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Composition

Use the following composition to build a questionnaire:

```text
Questionnaire
├── QuestionnaireProgress
├── QuestionnaireItem
│   ├── QuestionnaireTitle
│   ├── QuestionnaireDescription
│   ├── QuestionnaireChoices
│   │   ├── QuestionnaireChoice
│   │   │   └── QuestionnaireChoiceDescription
│   │   └── QuestionnaireInput
│   └── QuestionnaireError
└── QuestionnaireActions
    ├── QuestionnairePrevious
    ├── QuestionnaireSkip
    ├── QuestionnaireNext
    └── QuestionnaireSubmit
```

`Questionnaire` renders a real `<form>` and every item renders a `<fieldset>` with a `<legend>`, so answers submit with `FormData` and no extra state is needed.

## Server Rendering

Pass `items` to server-render the active item, progress, actions, and answer shortcuts. Without it the questionnaire only learns its order once the items have mounted on the client.

## Features

- One question at a time, with progress, navigation, and validation handled for you
- Single-choice, multiple-choice, freeform, and intentionally skipped answers
- Keyboard shortcuts for choices, plus arrow key navigation between questions and answers
- Declarative `items` for item order, conditional items, and stable shortcut assignment
- Controlled navigation with `v-model:item` for custom validation flows
- Native form reset restores the answers you marked as defaults

## Examples

### Multiple Selection

Use `multiple` for an item that accepts more than one fixed answer.

::component-preview
---
name: QuestionnaireMultipleDemo
class: style-nova
align: end
previewClass: min-h-[420px] p-4 sm:p-8
---
::

### Freeform Answer

Compose `QuestionnaireInput` with fixed choices when the user can provide another answer.

::component-preview
---
name: QuestionnaireFreeformDemo
class: style-nova
align: end
previewClass: min-h-[420px] p-4 sm:p-8
---
::

### Explicit Skip

Add `QuestionnaireSkip` when an optional item may be intentionally left unanswered.

::component-preview
---
name: QuestionnaireSkipDemo
class: style-nova
align: end
previewClass: min-h-[520px] p-4 sm:p-8
---
::

### Shortcuts

Assign a letter or number key to each answer with `shortcuts`. Declare `choices` on `items` so the keys stay stable regardless of the render order.

::component-preview
---
name: QuestionnaireShortcutsDemo
class: style-nova
align: end
previewClass: min-h-[480px] p-4 sm:p-8
---
::

### Custom Validation

Combine controlled navigation with an external schema such as Zod to return to an invalid item and present its error.

::component-preview
---
name: QuestionnaireValidationDemo
class: style-nova
align: end
previewClass: min-h-[520px] p-4 sm:p-8
---
::

`QuestionnaireError` falls back to a built-in message, so only render your own message when you have one:

```vue showLineNumbers
<QuestionnaireError>
  <template v-if="errors.detail">
    {{ errors.detail }}
  </template>
</QuestionnaireError>
```

### Controlled

Control the active item from host state, such as returning to an invalid step. Use `v-model:item`.

::component-preview
---
name: QuestionnaireControlledDemo
class: style-nova
align: end
previewClass: min-h-[520px] p-4 sm:p-8
---
::

### Resume

Restore a saved active item and default answers, then reset changes back to that saved state.

::component-preview
---
name: QuestionnaireResumeDemo
class: style-nova
align: end
previewClass: min-h-[520px] p-4 sm:p-8
---
::

### Conditional Items

Disable items that do not apply to the user's earlier answers.

::component-preview
---
name: QuestionnaireConditionalDemo
class: style-nova
align: end
previewClass: min-h-[520px] p-4 sm:p-8
---
::

### Navigation State

Read item status to opt into disabled navigation and custom action styling. Listen to `@update:status` on the items you want to track.

::component-preview
---
name: QuestionnaireNavigationStateDemo
class: style-nova
align: end
previewClass: min-h-[480px] p-4 sm:p-8
---
::

### Custom Progress

Use the progress slot state to build a custom progress indicator. `QuestionnaireProgress` exposes `current`, `total`, `first`, and `last`.

::component-preview
---
name: QuestionnaireProgressDemo
class: style-nova
align: end
previewClass: min-h-[520px] p-4 sm:p-8
---
::

### Animated Items

Animate the active item while keeping progress and navigation stationary. The active item is marked with `data-active`.

::component-preview
---
name: QuestionnaireAnimatedDemo
class: style-nova
align: end
previewClass: min-h-[520px] p-4 sm:p-8
---
::

### Card

Compose Questionnaire with Card slots while keeping the question title and description semantic. Use `as-child` to render a part as another component:

```vue showLineNumbers
<QuestionnaireTitle as-child>
  <CardTitle>What should the agent work on?</CardTitle>
</QuestionnaireTitle>
```

::component-preview
---
name: QuestionnaireCardDemo
class: style-nova
align: end
previewClass: min-h-[560px] p-4 sm:p-8
---
::

`QuestionnaireProgress`, `QuestionnaireTitle`, `QuestionnaireDescription`, `QuestionnaireChoices`, `QuestionnaireError`, `QuestionnaireActions`, and the four navigation buttons all accept `as` and `as-child`. `as-child` on the title replaces the `legend` that names the item, so the item labels itself with the rendered title instead. The title and description keep the id of the child they render.

### Dialog

Compose Questionnaire inside a Dialog while keeping cancellation and dismissal host-owned.

::component-preview
---
name: QuestionnaireDialogDemo
class: style-nova
align: end
previewClass: min-h-[320px] p-4 sm:p-8
---
::

## Keyboard navigation

| Key                    | Description                                                      |
| ---------------------- | ---------------------------------------------------------------- |
| `Arrow Down`           | Moves focus to the next answer.                                   |
| `Arrow Up`             | Moves focus to the previous answer.                               |
| `Arrow Right`          | Moves to the next question once the current one is answered.      |
| `Arrow Left`           | Moves to the previous question.                                   |
| `Enter`                | Confirms the focused answer and moves on, or submits on the last question. |
| `Meta+Enter` / `Ctrl+Enter` | Confirms the active question from anywhere in the form.      |
| `A` – `Z` / `1` – `9`  | Selects the matching choice when `shortcuts` is set.              |

## Accessibility

`QuestionnaireItem` renders a `fieldset` with a `legend`, so every question is announced with its answers. Descriptions and errors are associated with the item through `aria-describedby`, and an invalid item exposes `aria-invalid`.

`QuestionnaireProgress` renders a named `progressbar` that announces the current question. Inactive items are `hidden` and `inert`, so they stay out of the tab order and the accessibility tree.

Navigation actions are real buttons. `QuestionnaireSubmit` submits the form, so a questionnaire keeps working with browser autofill and native form submission.

## API Reference

### Questionnaire

The root form. Manages the active item, progress, validation, and keyboard navigation.

| Prop          | Type                            | Default | Description                                                                 |
| ------------- | ------------------------------- | ------- | --------------------------------------------------------------------------- |
| `items`       | `QuestionnaireItemDefinition[]`  | -       | Declares item order, `required`, `disabled`, and the `choices` used for shortcuts. |
| `item`        | `string`                        | -       | The active item. Use with `v-model:item`.                                    |
| `defaultItem` | `string`                        | -       | The item shown first. Ignored when `item` is provided.                       |
| `shortcuts`   | `"letters" \| "numbers"`        | -       | Assigns a keyboard shortcut to every choice.                                 |
| `noValidate`  | `boolean`                       | `true`  | Set to `false` to run native constraint validation on answered items.        |
| `class`       | `HTMLAttributes["class"]`       | -       | Additional classes to apply to the form.                                     |

| Emit           | Payload  | Description                                                              |
| -------------- | -------- | ------------------------------------------------------------------------ |
| `update:item`  | `string` | The active item changed.                                                 |
| `submit`       | `Event`  | Every item is valid. Call `event.preventDefault()` to handle it yourself. |
| `reset`        | `Event`  | The form was reset. Call `event.preventDefault()` to keep the answers.    |

### QuestionnaireProgress

A `progressbar` announcing the active question. Exposes `current`, `total`, `first`, and `last` to its default slot.

| Prop    | Type                      | Default | Description                                      |
| ------- | ------------------------- | ------- | ------------------------------------------------ |
| `class` | `HTMLAttributes["class"]` | -       | Additional classes to apply to the progress bar. |
| `as`      | `AsTag \| Component`      | `"div"`   | The element or component to render as.      |
| `asChild` | `boolean`                 | `false` | Render the child element instead.           |

### QuestionnaireItem

A single question, rendered as a `fieldset`. Only the active item is visible.

| Prop       | Type                      | Default | Description                                                   |
| ---------- | ------------------------- | ------- | ------------------------------------------------------------- |
| `name`     | `string`                  | -       | Required. The name the answer submits under.                   |
| `required` | `boolean`                 | `false` | Requires an answer before the questionnaire can continue.      |
| `multiple` | `boolean`                 | `false` | Renders choices as checkboxes and keeps every selected answer. |
| `disabled` | `boolean`                 | `false` | Removes the item from the flow without unmounting it.          |
| `invalid`  | `boolean`                 | `false` | Marks the item invalid from outside, for example after schema validation. |
| `class`    | `HTMLAttributes["class"]` | -       | Additional classes to apply to the item.                       |

| Emit             | Payload                                       | Description             |
| ---------------- | --------------------------------------------- | ----------------------- |
| `update:status`  | `"unanswered" \| "answered" \| "skipped"`     | The item status changed. |

### QuestionnaireTitle

The question, rendered as a `legend`.

| Prop    | Type                      | Default | Description                                 |
| ------- | ------------------------- | ------- | ------------------------------------------- |
| `class` | `HTMLAttributes["class"]` | -       | Additional classes to apply to the title.   |
| `as`      | `AsTag \| Component`      | `"legend"`   | The element or component to render as.      |
| `asChild` | `boolean`                 | `false` | Render the child element instead.           |

### QuestionnaireDescription

Help text associated with the item through `aria-describedby`.

| Prop    | Type                      | Default | Description                                     |
| ------- | ------------------------- | ------- | ----------------------------------------------- |
| `id`    | `string`                  | -       | Overrides the generated id.                     |
| `as`      | `AsTag \| Component`      | `"p"`   | The element or component to render as.      |
| `asChild` | `boolean`                 | `false` | Render the child element instead.           |
| `class` | `HTMLAttributes["class"]` | -       | Additional classes to apply to the description. |

### QuestionnaireChoices

The answer list. Wraps choices and an optional freeform input.

| Prop    | Type                      | Default | Description                                 |
| ------- | ------------------------- | ------- | ------------------------------------------- |
| `class` | `HTMLAttributes["class"]` | -       | Additional classes to apply to the list.    |
| `as`      | `AsTag \| Component`      | `"div"`   | The element or component to render as.      |
| `asChild` | `boolean`                 | `false` | Render the child element instead.           |

### QuestionnaireChoice

A single answer, rendered as a radio or a checkbox depending on the item.

| Prop             | Type                      | Default | Description                                             |
| ---------------- | ------------------------- | ------- | ------------------------------------------------------- |
| `value`          | `string`                  | -       | Required. The submitted value.                           |
| `checked`        | `boolean`                 | -       | Controlled checked state. Use with `v-model:checked`.    |
| `defaultChecked` | `boolean`                 | `false` | Checks the choice on mount and after a native form reset.|
| `disabled`       | `boolean`                 | `false` | Disables the choice.                                     |
| `class`          | `HTMLAttributes["class"]` | -       | Additional classes to apply to the choice.               |

| Emit               | Payload   | Description                        |
| ------------------ | --------- | ---------------------------------- |
| `update:checked`   | `boolean` | The choice was checked or cleared. |
| `change`           | `Event`   | The native change event.           |

### QuestionnaireChoiceDescription

Secondary text inside a choice.

| Prop    | Type                      | Default | Description                                     |
| ------- | ------------------------- | ------- | ----------------------------------------------- |
| `class` | `HTMLAttributes["class"]` | -       | Additional classes to apply to the description. |

### QuestionnaireInput

A freeform answer. Answers the item while it holds a value, and submits under the item name.

| Prop           | Type                      | Default  | Description                                           |
| -------------- | ------------------------- | -------- | ----------------------------------------------------- |
| `modelValue`   | `string \| number`        | -        | Controlled value. Use with `v-model`.                 |
| `defaultValue` | `string \| number`        | -        | Fills the answer on mount and after a native reset.   |
| `type`         | `QuestionnaireInputType`  | `"text"` | The input type.                                       |
| `disabled`     | `boolean`                 | `false`  | Disables the input.                                   |
| `class`        | `HTMLAttributes["class"]` | -        | Additional classes to apply to the input.             |

### QuestionnaireError

The item error. Hidden until the item is invalid, and falls back to a built-in message.

| Prop    | Type                      | Default | Description                               |
| ------- | ------------------------- | ------- | ----------------------------------------- |
| `id`    | `string`                  | -       | Overrides the generated id.               |
| `as`      | `AsTag \| Component`      | `"p"`   | The element or component to render as.      |
| `asChild` | `boolean`                 | `false` | Render the child element instead.           |
| `class` | `HTMLAttributes["class"]` | -       | Additional classes to apply to the error. |

### QuestionnaireActions

The navigation row.

| Prop    | Type                      | Default | Description                                 |
| ------- | ------------------------- | ------- | ------------------------------------------- |
| `class` | `HTMLAttributes["class"]` | -       | Additional classes to apply to the row.     |
| `as`      | `AsTag \| Component`      | `"div"`   | The element or component to render as.      |
| `asChild` | `boolean`                 | `false` | Render the child element instead.           |

### QuestionnairePrevious, QuestionnaireSkip, QuestionnaireNext, and QuestionnaireSubmit

Navigation buttons. Each one hides itself when it does not apply: `QuestionnairePrevious` on the first item, `QuestionnaireSkip` on required items, `QuestionnaireNext` on the last item, and `QuestionnaireSubmit` everywhere but the last item.

| Prop       | Type                            | Default                            | Description                                |
| ---------- | ------------------------------- | ---------------------------------- | ------------------------------------------ |
| `variant`  | `ButtonVariants["variant"]`     | `"outline"` / `"default"`          | The button variant.                        |
| `size`     | `ButtonVariants["size"]`        | `"default"`                        | The button size.                            |
| `disabled` | `boolean`                       | `false`                            | Disables the button.                        |
| `as`       | `AsTag \| Component`            | `"button"`                         | The element or component to render as.      |
| `asChild`  | `boolean`                       | `false`                            | Render the child element instead.           |
| `class`    | `HTMLAttributes["class"]`       | -                                  | Additional classes to apply to the button.  |

---

### Apps V4 Content Docs Components Radio Group (`apps/v4/content/docs/components/radio-group.md`)

---
title: Radio Group
description: A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.
component: true
links:
  doc: https://reka-ui.com/docs/components/radio-group
  api: https://reka-ui.com/docs/components/radio-group#api-reference
---

::component-preview
---
name: RadioGroupDemo
description: A radio group component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add radio-group
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/radio-group) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Label } from '@/components/ui/label'
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
</script>

<template>
  <RadioGroup default-value="comfortable">
    <div class="flex items-center space-x-2">
      <RadioGroupItem id="r1" value="default" />
      <Label for="r1">Default</Label>
    </div>
    <div class="flex items-center space-x-2">
      <RadioGroupItem id="r2" value="comfortable" />
      <Label for="r2">Comfortable</Label>
    </div>
    <div class="flex items-center space-x-2">
      <RadioGroupItem id="r3" value="compact" />
      <Label for="r3">Compact</Label>
    </div>
  </RadioGroup>
</template>
```

---

### Apps V4 Content Docs Components Range Calendar (`apps/v4/content/docs/components/range-calendar.md`)

---
title: Range Calendar
description: Presents a calendar view tailored for selecting date ranges.
component: true
links:
  doc: https://reka-ui.com/docs/components/range-calendar
  api: https://reka-ui.com/docs/components/range-calendar#api-reference
---

::component-preview
---
name: CalendarRangeDemo
previewClass: h-[42rem] sm:h-[26rem]
---
::

## About

The `<RangeCalendar />` component is built on top of the [Reka UI Range Calendar](https://www.reka-ui.com/docs/components/date-range-picker.html) component, which uses the [@internationalized/date](https://react-spectrum.adobe.com/internationalized/date/index.html) package to handle dates.

If you're looking for a normal calendar, check out the [Calendar](./calendar.md) component.

## Installation
:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add range-calendar
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/range-calendar) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage
```vue
<script setup lang="ts">
import { RangeCalendar } from "@/components/ui/range-calendar";
</script>

<template>
  <RangeCalendar />
</template>
```

---

### Apps V4 Content Docs Components Resizable (`apps/v4/content/docs/components/resizable.md`)

---
title: Resizable
description: Accessible resizable panel groups and layouts with keyboard support.
component: true
links:
  doc: https://reka-ui.com/docs/components/splitter
  api: https://reka-ui.com/docs/components/splitter#api-reference
---

::component-preview
---
name: ResizableDemo
description: A resizable component.
previewClass: h-80
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add resizable
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/resizable) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  ResizableHandle,
  ResizablePanel,
  ResizablePanelGroup,
} from '@/components/ui/resizable'
</script>

<template>
  <ResizablePanelGroup direction="horizontal">
    <ResizablePanel>One</ResizablePanel>
    <ResizableHandle />
    <ResizablePanel>Two</ResizablePanel>
  </ResizablePanelGroup>
</template>
```

---

### Apps V4 Content Docs Components Scroll Area (`apps/v4/content/docs/components/scroll-area.md`)

---
title: Scroll Area
description: Augments native scroll functionality for custom, cross-browser styling.
component: true
links:
  doc: https://reka-ui.com/docs/components/scroll-area
  api: https://reka-ui.com/docs/components/scroll-area#api-reference
---

::component-preview
---
name: ScrollAreaDemo
description: A scroll area component.
previewClass: h-96
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add scroll-area
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/scroll-area) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { ScrollArea } from '@/components/ui/scroll-area'
</script>

<template>
  <ScrollArea class="h-[200px] w-[350px] rounded-md border p-4">
    Jokester began sneaking into the castle in the middle of the night and leaving
    jokes all over the place: under the king's pillow, in his soup, even in the
    royal toilet. The king was furious, but he couldn't seem to stop Jokester. And
    then one day, the king tripped over one of Jokester's whoopee cushions and
    fell into the moat. He was so embarrassed that he decided to make Jokester the
    official court jester.
  </ScrollArea>
</template>
```

---

### Apps V4 Content Docs Components Select (`apps/v4/content/docs/components/select.md`)

---
title: Select
description: Displays a list of options for the user to pick from—triggered by a button.
component: true
links:
  doc: https://reka-ui.com/docs/components/select
  api: https://reka-ui.com/docs/components/select#api-reference
---

::component-preview
---
name: SelectDemo
description: A select component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add select
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/select) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select'
</script>

<template>
  <Select>
    <SelectTrigger>
      <SelectValue placeholder="Select a fruit" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="apple">
        Apple
      </SelectItem>
      <SelectItem value="banana">
        Banana
      </SelectItem>
      <SelectItem value="blueberry">
        Blueberry
      </SelectItem>
      <SelectItem value="grapes">
        Grapes
      </SelectItem>
      <SelectItem value="pineapple">
        Pineapple
      </SelectItem>
    </SelectContent>
  </Select>
</template>
```

## Examples

### Scrollable

::component-preview
---
name: SelectScrollable
description: A scrollable select component with timezone options.
---
::

### Multiple

::component-preview
---
name: SelectMultipleDemo
description: A multiple select component
---
::

---

### Apps V4 Content Docs Components Separator (`apps/v4/content/docs/components/separator.md`)

---
title: Separator
description: Visually or semantically separates content.
component: true
links:
  doc: https://reka-ui.com/docs/components/separator
  api: https://reka-ui.com/docs/components/separator#api-reference
---

::component-preview
---
name: SeparatorDemo
description: A separator component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add separator
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/separator) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Separator } from '@/components/ui/separator'
</script>

<template>
  <div>
    <div class="space-y-1">
      <h4 class="text-sm font-medium leading-none">
        Radix Primitives
      </h4>
      <p class="text-sm text-muted-foreground">
        An open-source UI component library.
      </p>
    </div>
    <Separator class="my-4" />
    <div class="flex h-5 items-center space-x-4 text-sm">
      <div>Blog</div>
      <Separator orientation="vertical" />
      <div>Docs</div>
      <Separator orientation="vertical" />
      <div>Source</div>
    </div>
  </div>
</template>
```

---

### Apps V4 Content Docs Components Sheet (`apps/v4/content/docs/components/sheet.md`)

---
title: Sheet
description: Extends the Dialog component to display content that complements the main content of the screen.
component: true
links:
  doc: https://reka-ui.com/docs/components/dialog
  api: https://reka-ui.com/docs/components/dialog#api-reference
---

::component-preview
---
name: SheetDemo
description: A sheet component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add sheet
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/sheet) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet'
</script>

<template>
  <Sheet>
    <SheetTrigger>Open</SheetTrigger>
    <SheetContent>
      <SheetHeader>
        <SheetTitle>Are you absolutely sure?</SheetTitle>
        <SheetDescription>
          This action cannot be undone. This will permanently delete your account
          and remove your data from our servers.
        </SheetDescription>
      </SheetHeader>
    </SheetContent>
  </Sheet>
</template>
```

---

### Apps V4 Content Docs Components Sidebar (`apps/v4/content/docs/components/sidebar.md`)

---
title: Sidebar
description: A composable, themeable and customizable sidebar component.
component: true
---



::component-preview
---
title: Sidebar
name: sidebar-07
description: A composable, themeable and customizable sidebar component built using shadcn/vue
class: 'w-full'
type: block
---
::

::vue-school-link{class="mt-6" lesson="sidebars-in-shadcn-vue" placement="top"}
Watch a Vue School video about building sidebars with shadcn-vue.
::

Sidebars are one of the most complex components to build. They are central
to any application and often contain a lot of moving parts.

I don't like building sidebars. So I built 30+ of them. All kinds of
configurations. Then I extracted the core components into `Sidebar*.vue`.

We now have a solid foundation to build on top of. Composable. Themeable.
Customizable.

[Browse the Blocks Library](/blocks).

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add sidebar
```

::

::::tabs-content{value="manual"}

  :::steps
    ::step
    Install this component
    ::

    ```bash
    npx shadcn-vue@latest add sidebar
    ```

    ::step
    Add the following colors to your CSS file
    ::

    The command above should install the colors for you. If not, copy and paste the following in your CSS file.

    ```css
    @layer base {
      :root {
        --sidebar-background: 0 0% 98%;
        --sidebar-foreground: 240 5.3% 26.1%;
        --sidebar-primary: 240 5.9% 10%;
        --sidebar-primary-foreground: 0 0% 98%;
        --sidebar-accent: 240 4.8% 95.9%;
        --sidebar-accent-foreground: 240 5.9% 10%;
        --sidebar-border: 220 13% 91%;
        --sidebar-ring: 217.2 91.2% 59.8%;
     }

      .dark {
        --sidebar-background: 240 5.9% 10%;
        --sidebar-foreground: 240 4.8% 95.9%;
        --sidebar-primary: 224.3 76.3% 48%;
        --sidebar-primary-foreground: 0 0% 100%;
        --sidebar-accent: 240 3.7% 15.9%;
        --sidebar-accent-foreground: 240 4.8% 95.9%;
        --sidebar-border: 240 3.7% 15.9%;
        --sidebar-ring: 217.2 91.2% 59.8%;
      }
    }
    ```
  :::

::::

:::::

## Structure

A `Sidebar` component is composed of the following parts:

- `SidebarProvider` - Handles collapsible state.
- `Sidebar` - The sidebar container.
- `SidebarHeader` and SidebarFooter - Sticky at the top and bottom of the sidebar
- `SidebarContent` - Scrollable content.
- `SidebarGroup` - Section within the SidebarContent.
- `SidebarTrigger` - Trigger for the Sidebar

<img
  src="/images/sidebar-structure.png"
  width="716"
  height="420"
  alt="Sidebar Structure"
  class="border dark:hidden rounded-lg overflow-hidden mt-6 w-full"
/>
<img
  src="/images/sidebar-structure-dark.png"
  width="716"
  height="420"
  alt="Sidebar Structure"
  class="border hidden dark:block rounded-lg overflow-hidden mt-6 w-full"
/>

## Usage

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Your First Sidebar

Let's start with the most basic sidebar A collapsible sidebar with a menu.

:::steps

  ::step
  Add a `SidebarProvider` and `SidebarTrigger` at the root of your application
  ::

  ```vue showLineNumbers
  <script setup lang="ts">
  import AppSidebar from '@/components/AppSidebar.vue'
  import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'
  </script>

  <template>
    <SidebarProvider>
      <AppSidebar />
      <main>
        <SidebarTrigger />
        <slot />
      </main>
    </SidebarProvider>
  </template>
  ```

  ::step
  Create a new sidebar component at `@/components/AppSidebar.vue`
  ::

  ```vue showLineNumbers
  <script setup lang="ts">
  import { Sidebar, SidebarContent } from '@/components/ui/sidebar'
  </script>

  <template>
    <Sidebar>
      <SidebarContent />
    </Sidebar>
  </template>
  ```

  ::step
  Now, let's add a `SidebarMenu` to the sidebar
  ::

  We'll use the `SidebarMenu` component in a `SidebarGroup`.

  ```vue showLineNumbers
  <script setup lang="ts">
  import { Calendar, Home, Inbox, Search, Settings } from '@lucide/vue'
  import {
    Sidebar,
    SidebarContent,
    SidebarGroup,
    SidebarGroupContent,
    SidebarGroupLabel,
    SidebarMenu,
    SidebarMenuButton,
    SidebarMenuItem,
  } from '@/components/ui/sidebar'

  // Menu items.
  const items = [
    {
      title: 'Home',
      url: '#',
      icon: Home,
    },
    {
      title: 'Inbox',
      url: '#',
      icon: Inbox,
    },
    {
      title: 'Calendar',
      url: '#',
      icon: Calendar,
    },
    {
      title: 'Search',
      url: '#',
      icon: Search,
    },
    {
      title: 'Settings',
      url: '#',
      icon: Settings,
    },
  ]
  </script>

  <template>
    <Sidebar>
      <SidebarContent>
        <SidebarGroup>
          <SidebarGroupLabel>Application</SidebarGroupLabel>
          <SidebarGroupContent>
            <SidebarMenu>
              <SidebarMenuItem v-for="item in items" :key="item.title">
                <SidebarMenuButton as-child>
                  <a :href="item.url">
                    <component :is="item.icon" />
                    <span>{{ item.title }}</span>
                  </a>
                </SidebarMenuButton>
              </SidebarMenuItem>
            </SidebarMenu>
          </SidebarGroupContent>
        </SidebarGroup>
      </SidebarContent>
    </Sidebar>
  </template>
  ```

  ::step
  You've created your first sidebar.
  ::

  You should see something like this:

<figure class="flex flex-col gap-4">

  ::component-preview
  ---
  title: Sidebar
  name: sidebar-demo
  description: Your first sidebar.
  class: 'w-full'
  type: block
  ---
  ::

  <figcaption class="text-center text-sm text-gray-500">
    Your first sidebar
  </figcaption>
</figure>

:::

## Components

The components in the `Sidebar*.vue` files are built to be composable i.e you build your sidebar by putting the provided components together. They also compose well with other shadcn-vue components such as `DropdownMenu`, `Collapsible`, `Dialog`, etc.

**If you need to change the code in the `Sidebar*.vue` files, you are encourage to do so. The code is yours. Use the provided components as a starting point to build your own**

## SidebarProvider

The `SidebarProvider` component is used to provide the sidebar context to all its children.

### Props

The `SidebarProvider` component accepts the following props:

### Width

Use the `defaultOpen`, `open`, and `onOpenChange` props to control the open state of the sidebar.

```vue showLineNumbers
<script setup lang="ts">
import { ref } from 'vue'

const open = ref(false)
</script>

<template>
  <SidebarProvider :open="open" @update:open="open = $event">
    
  </SidebarProvider>
</template>
```

### Keyboard Shortcut

The `SidebarProvider` component supports keyboard shortcuts to toggle the sidebar. The default shortcut is `cmd+b` or `ctrl+b`.

```vue showLineNumbers
<SidebarProvider>
  
</SidebarProvider>
```

### Persisted State

To persist the sidebar state, you can use the `storageKey` prop on the `SidebarProvider` component.

```vue showLineNumbers
<SidebarProvider storage-key="sidebar">
  
</SidebarProvider>
```

```vue showLineNumbers
<SidebarProvider
  :default-open="false"
  storage-key="sidebar"
  class="flex min-h-screen"
>
  
</SidebarProvider>
```

## Sidebar

The main sidebar component.

```vue showLineNumbers
<Sidebar>
  <SidebarHeader />
  <SidebarContent />
  <SidebarFooter />
</Sidebar>
```

### Props

The `Sidebar` component accepts the following props:

### side

Use the `side` prop to set the side of the sidebar.

```vue showLineNumbers
<Sidebar side="left">
  
</Sidebar>
```

### variant

Use the `variant` prop to set the variant of the sidebar.

```vue showLineNumbers

<Sidebar variant="sidebar">
  
</Sidebar>
```

```vue showLineNumbers

<Sidebar variant="floating">
  
</Sidebar>
```

```vue showLineNumbers

<Sidebar variant="inset">
  
</Sidebar>
```

### collapsible

Use the `collapsible` prop to make the sidebar collapsible.

```vue showLineNumbers
<Sidebar collapsible="icon">
  
</Sidebar>
```

```vue showLineNumbers
<Sidebar collapsible="offcanvas">
  
</Sidebar>
```

## useSidebar

The `useSidebar` hook is used to control the sidebar.

```vue showLineNumbers
<script setup lang="ts">
import { useSidebar } from '@/components/ui/sidebar'

const {
  state,
  open,
  setOpen,
  openMobile,
  setOpenMobile,
  isMobile,
  toggleSidebar,
} = useSidebar()
</script>
```

## SidebarHeader

Used to render the sidebar header.

```vue showLineNumbers
<Sidebar>
  <SidebarHeader>
    <SidebarMenu>
      <SidebarMenuItem>
        <SidebarMenuButton size="lg">
          <div class="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
            <GalleryVerticalEnd class="size-4" />
          </div>
          <div class="flex flex-col gap-0.5 leading-none">
            <span class="font-semibold">Documentation</span>
            <span class="">v1.0.0</span>
          </div>
        </SidebarMenuButton>
      </SidebarMenuItem>
    </SidebarMenu>
  </SidebarHeader>
</Sidebar>
```

## SidebarFooter

Used to render the sidebar footer.

```vue showLineNumbers
<Sidebar>
  <SidebarFooter>
    <SidebarMenu>
      <SidebarMenuItem>
        <DropdownMenu>
          <DropdownMenuTrigger as-child>
            <SidebarMenuButton>
              <User2 /> Username
              <ChevronUp class="ml-auto" />
            </SidebarMenuButton>
          </DropdownMenuTrigger>
          <DropdownMenuContent
            side="top"
            class="w-(--reka-popper-anchor-width)"
          >
            <DropdownMenuItem>
              <span>Account</span>
            </DropdownMenuItem>
            <DropdownMenuItem>
              <span>Billing</span>
            </DropdownMenuItem>
            <DropdownMenuItem>
              <span>Sign out</span>
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </SidebarMenuItem>
    </SidebarMenu>
  </SidebarFooter>
</Sidebar>
```

## SidebarContent

The scrollable content area of the sidebar.

```vue showLineNumbers
<Sidebar>
  <SidebarContent>
    <SidebarGroup />
    <SidebarGroup />
  </SidebarContent>
</Sidebar>
```

## SidebarGroup

Used to group sidebar menu items.

```vue showLineNumbers
<SidebarContent>
  <SidebarGroup>
    <SidebarGroupLabel>Application</SidebarGroupLabel>
    <SidebarGroupContent>
      
    </SidebarGroupContent>
  </SidebarGroup>
</SidebarContent>
```

## Collapsible SidebarGroup

To make a `SidebarGroup` collapsible, wrap it in a `Collapsible` component.

```vue showLineNumbers
<SidebarGroup as-child>
  <Collapsible default-open class="group/collapsible">
    <SidebarGroupLabel as-child>
      <CollapsibleTrigger class="group/label w-full text-left text-sm text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground [&[data-state=open]>svg]:rotate-90">
        Help
        <ChevronRight class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90" />
      </CollapsibleTrigger>
    </SidebarGroupLabel>
    <CollapsibleContent>
      <SidebarGroupContent>
        <SidebarMenu>
          
        </SidebarMenu>
      </SidebarGroupContent>
    </CollapsibleContent>
  </Collapsible>
</SidebarGroup>
```

## SidebarGroupAction

The `SidebarGroupAction` component is used to render an action button in the sidebar group header.

```vue showLineNumbers
<SidebarGroup>
  <SidebarGroupLabel>
    Projects
    <SidebarGroupAction>
      <Plus /> <span class="sr-only">Add Project</span>
    </SidebarGroupAction>
  </SidebarGroupLabel>
  <SidebarGroupContent></SidebarGroupContent>
</SidebarGroup>
```

## SidebarMenu

The `SidebarMenu` component is used to render a menu in the sidebar.

```vue showLineNumbers
<SidebarGroupContent>
  <SidebarMenu>
    <SidebarMenuItem>
      <SidebarMenuButton as-child>
        <a href="#">
          <Home />
          <span>Home</span>
        </a>
      </SidebarMenuButton>
    </SidebarMenuItem>
    <SidebarMenuItem>
      <SidebarMenuButton as-child>
        <a href="#">
          <Inbox />
          <span>Inbox</span>
        </a>
      </SidebarMenuButton>
    </SidebarMenuItem>
  </SidebarMenu>
</SidebarGroupContent>
```

## SidebarMenuButton

The `SidebarMenuButton` component is used to render a menu button in the sidebar.

### Link or Anchor

Use the `as-child` prop to render the `SidebarMenuButton` as a link or anchor.

```vue showLineNumbers
<SidebarMenuButton as-child>
  <a href="#">
    <Home />
    <span>Home</span>
  </a>
</SidebarMenuButton>
```

### Icon and Label

You can render an icon and label in the `SidebarMenuButton` component.

```vue showLineNumbers
<SidebarMenuButton>
  <Home />
  <span>Home</span>
</SidebarMenuButton>
```

### isActive

Use the `isActive` prop to mark a menu button as active.

```vue showLineNumbers
<SidebarMenuButton :is-active="true">
  <Home />
  <span>Home</span>
</SidebarMenuButton>
```

## SidebarMenuAction

The `SidebarMenuAction` component is used to render a menu action in the sidebar.

```vue showLineNumbers
<SidebarMenuItem>
  <SidebarMenuButton>
    <Home />
    <span>Home</span>
  </SidebarMenuButton>
  <SidebarMenuAction>
    <MoreHorizontal />
  </SidebarMenuAction>
</SidebarMenuItem>
```

### DropdownMenu

You can use the `SidebarMenuAction` component with a `DropdownMenu` component.

```vue showLineNumbers
<SidebarMenuItem>
  <SidebarMenuButton>
    <Home />
    <span>Home</span>
  </SidebarMenuButton>
  <DropdownMenu>
    <DropdownMenuTrigger as-child>
      <SidebarMenuAction>
        <MoreHorizontal />
      </SidebarMenuAction>
    </DropdownMenuTrigger>
    <DropdownMenuContent side="right" align="start">
      <DropdownMenuItem>
        <span>Edit Project</span>
      </DropdownMenuItem>
      <DropdownMenuItem>
        <span>Delete Project</span>
      </DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</SidebarMenuItem>
```

## SidebarMenuSub

The `SidebarMenuSub` component is used to render a submenu in the sidebar.

```vue showLineNumbers
<SidebarMenuItem>
  <SidebarMenuButton>
    <Home />
    <span>Home</span>
  </SidebarMenuButton>
  <SidebarMenuSub>
    <SidebarMenuItem>
      <SidebarMenuButton>
        <span>History</span>
      </SidebarMenuButton>
    </SidebarMenuItem>

    <SidebarMenuItem>
      <SidebarMenuButton>
        <span>Starred</span>
      </SidebarMenuButton>
    </SidebarMenuItem>
  </SidebarMenuSub>
</SidebarMenuItem>
```

## Collapsible SidebarMenu

To make a `SidebarMenu` collapsible, wrap it in a `Collapsible` component.

```vue showLineNumbers
<SidebarMenuItem>
  <Collapsible default-open class="group/collapsible">
    <CollapsibleTrigger as-child>
      <SidebarMenuButton>
        <Home />
        <span>Home</span>
        <ChevronRight class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90" />
      </SidebarMenuButton>
    </CollapsibleTrigger>
    <CollapsibleContent>
      <SidebarMenuSub>
        <SidebarMenuItem>
          <SidebarMenuButton>
            <span>History</span>
          </SidebarMenuButton>
        </SidebarMenuItem>

        <SidebarMenuItem>
          <SidebarMenuButton>
            <span>Starred</span>
          </SidebarMenuButton>
        </SidebarMenuItem>
      </SidebarMenuSub>
    </CollapsibleContent>
  </Collapsible>
</SidebarMenuItem>
```

## SidebarMenuBadge

The `SidebarMenuBadge` component is used to render a badge in the sidebar menu.

```vue showLineNumbers
<SidebarMenuButton>
  <Home />
  <span>Home</span>
  <SidebarMenuBadge>24</SidebarMenuBadge>
</SidebarMenuButton>
```

## SidebarMenuSkeleton

You can use the `SidebarMenuSkeleton` component to render a skeleton loader in the sidebar menu.

```vue showLineNumbers
<SidebarMenu>
  <SidebarMenuItem v-for="item in Array.from({ length: 5 })" :key="item">
    <SidebarMenuSkeleton />
  </SidebarMenuItem>
</SidebarMenu>
```

## SidebarSeparator

The `SidebarSeparator` component is used to render a separator in the sidebar.

```vue showLineNumbers
<SidebarContent>
  <SidebarGroup />
  <SidebarSeparator />
  <SidebarGroup />
</SidebarContent>
```

## SidebarTrigger

The `SidebarTrigger` component is used to render a trigger button for the sidebar.

```vue showLineNumbers
<SidebarTrigger />
```

## Custom Trigger

You can create a custom trigger using the `useSidebar` hook.

```vue showLineNumbers
<script setup lang="ts">
import { useSidebar } from '@/components/ui/sidebar'

const { toggleSidebar } = useSidebar()
</script>

<template>
  <Button @click="toggleSidebar">
    Toggle Sidebar
  </Button>
</template>
```

## SidebarRail

The `SidebarRail` component is used to render a rail in the sidebar. This is usually used to toggle the sidebar on hover when the sidebar is collapsed.

```vue showLineNumbers
<Sidebar collapsible="icon">
  <SidebarHeader />
  <SidebarContent />
  <SidebarFooter />
  <SidebarRail />
</Sidebar>
```

## Controlled Sidebar

Use the `open` and `onOpenChange` props to control the sidebar.

```vue showLineNumbers
<script setup lang="ts">
import { ref } from 'vue'

const open = ref(false)
</script>

<template>
  <SidebarProvider :open="open" @update:open="open = $event">
    <Sidebar />
  </SidebarProvider>
</template>
```

## Theming

You can theme the sidebar using CSS variables.

```css
@layer base {
  :root {
    --sidebar-background: 0 0% 98%;
    --sidebar-foreground: 240 5.3% 26.1%;
    --sidebar-primary: 240 5.9% 10%;
    --sidebar-primary-foreground: 0 0% 98%;
    --sidebar-accent: 240 4.8% 95.9%;
    --sidebar-accent-foreground: 240 5.9% 10%;
    --sidebar-border: 220 13% 91%;
    --sidebar-ring: 217.2 91.2% 59.8%;
  }

  .dark {
    --sidebar-background: 240 5.9% 10%;
    --sidebar-foreground: 240 4.8% 95.9%;
    --sidebar-primary: 224.3 76.3% 48%;
    --sidebar-primary-foreground: 0 0% 100%;
    --sidebar-accent: 240 3.7% 15.9%;
    --sidebar-accent-foreground: 240 4.8% 95.9%;
    --sidebar-border: 240 3.7% 15.9%;
    --sidebar-ring: 217.2 91.2% 59.8%;
  }
}
```

## Styling

Here are some tips for styling the sidebar:

- Use the `data-sidebar` and `data-state` attributes to style the sidebar.
- The sidebar automatically sets the `--sidebar-width` CSS variable. You can use this to adjust the layout of your main content.

---

### Apps V4 Content Docs Components Skeleton (`apps/v4/content/docs/components/skeleton.md`)

---
title: Skeleton
description: Use to show a placeholder while content is loading.
component: true
---

::component-preview
---
name: SkeletonDemo
description: A skeleton component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add skeleton
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/skeleton) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Skeleton } from '@/components/ui/skeleton'
</script>

<template>
  <Skeleton class="w-[100px] h-[20px] rounded-full" />
</template>
```

---

### Apps V4 Content Docs Components Slider (`apps/v4/content/docs/components/slider.md`)

---
title: Slider
description: An input where the user selects a value from within a given range.
component: true
links:
  doc: https://reka-ui.com/docs/components/slider
  api: https://reka-ui.com/docs/components/slider#api-reference
---

::component-preview
---
name: SliderDemo
description: A slider component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add slider
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/slider) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Slider } from '@/components/ui/slider'
</script>

<template>
  <Slider :default-value="[33]" :max="100" :step="1" />
</template>
```

---

### Apps V4 Content Docs Components Sonner (`apps/v4/content/docs/components/sonner.md`)

---
title: Sonner
description: An opinionated toast component for Vue.
component: true
links:
  doc: https://vue-sonner.vercel.app/
---

::component-preview
---
name: SonnerDemo
description: A sonner toast component.
---
::

::vue-school-link{class="mt-6" lesson="data-tables-and-sonner-in-shadcn-vue" placement="top"}
Watch a Vue School video about data tables and Sonner in shadcn-vue.
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add sonner
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install vue-sonner
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/sonner) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { toast } from 'vue-sonner'
import { Button } from '@/components/ui/button'
</script>

<template>
  <Button @click="() => toast('My first toast')">
    Give me a toast
  </Button>
</template>
```

## Installation

The `<Toaster />` component and its specific CSS are required to display toasts. Add them to your root layout.

```vue showLineNumbers
<script setup lang="ts">
import 'vue-sonner/style.css'
import { Toaster } from '@/components/ui/sonner'
</script>

<template>
  <div>
    <main>
      
    </main>
    <Toaster />
  </div>
</template>
```

## Examples

### Types

::component-preview
---
name: SonnerTypesDemo
---
::

### With Dialog

::component-preview
---
name: SonnerWithDialogDemo
---
::

---

### Apps V4 Content Docs Components Spinner (`apps/v4/content/docs/components/spinner.md`)

---
title: Spinner
description: An indicator that can be used to show a loading state.
component: true
---

::component-preview
---
name: SpinnerDemo
class: '[&_.preview]:p-6'
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add spinner
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/spinner) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Spinner } from '@/components/ui/Spinner'
</script>

<template>
  <Spinner />
</template>
```

## Customization

You can replace the default spinner icon with any other icon by editing the `Spinner` component.

::component-preview
---
name: SpinnerCustomDemo
---
::

## Examples

### Size

Use the `size-*` utility class to change the size of the spinner.

::component-preview
---
name: SpinnerSizeDemo
---
::

### Color

Use the `text-*` utility class to change the color of the spinner.

::component-preview
---
name: SpinnerColorDemo
---
::

### Button

Add a spinner to a button to indicate a loading state. The `<Button />` will handle the spacing between the spinner and the text.

::component-preview
---
name: SpinnerButtonDemo
---
::

### Badge

You can also use a spinner inside a badge.

::component-preview
---
name: SpinnerBadgeDemo
---
::

### Input Group

Input Group can have spinners inside `<InputGroupAddon>`.

::component-preview
---
name: SpinnerInputGroupDemo
---
::

### Empty

You can place a spinner inside an empty state.

::component-preview
---
name: SpinnerEmptyDemo
previewClass: h-96
---
::

### Item

Use the spinner inside `<ItemMedia>` to indicate a loading state.

::component-preview
---
name: SpinnerItemDemo
---
::

## API Reference

### Spinner

Use the `Spinner` component to display a spinner.

| Prop        | Type     | Default |
| ----------- | -------- | ------- |
| `class`     | `string` |         |

```vue
<template>
  <Spinner />
</template>
```

---

### Apps V4 Content Docs Components Stepper (`apps/v4/content/docs/components/stepper.md`)

---
title: Stepper
description: A set of steps that are used to indicate progress through a multi-step process.
component: true
links:
  doc: https://reka-ui.com/docs/components/stepper
  api: https://reka-ui.com/docs/components/stepper#api-reference
---

::component-preview
---
name: StepperDemo
description: A stepper component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add stepper
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/stepper) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue
<script setup lang="ts">
import {
  Stepper,
  StepperDescription,
  StepperIndicator,
  StepperItem,
  StepperSeparator,
  StepperTitle,
  StepperTrigger,
} from '@/components/ui/stepper'
</script>

<template>
  <Stepper>
    <StepperItem :step="1">
      <StepperTrigger>
        <StepperIndicator>1</StepperIndicator>
        <StepperTitle>Step 1</StepperTitle>
        <StepperDescription>This is the first step</StepperDescription>
      </StepperTrigger>
      <StepperSeparator />
    </StepperItem>
    <StepperItem :step="2">
      <StepperTrigger>
        <StepperIndicator>2</StepperIndicator>
        <StepperTitle>Step 2</StepperTitle>
        <StepperDescription>This is the second step</StepperDescription>
      </StepperTrigger>
    </StepperItem>
  </Stepper>
</template>
```

## Examples

### Horizontal

::component-preview
---
name: StepperHorizental
description: A horizental stepper component.
---
::

### Vertical

::component-preview
---
name: StepperVertical
description: A vertical stepper component.
previewClass: h-96
---
::

### Form

::component-preview
---
name: StepperForm
description: A form stepper component.
previewClass: h-[28rem]
---
::

---

### Apps V4 Content Docs Components Switch (`apps/v4/content/docs/components/switch.md`)

---
title: Switch
description: A control that allows the user to toggle between checked and not checked.
component: true
links:
  doc: https://reka-ui.com/docs/components/switch
  api: https://reka-ui.com/docs/components/switch#api-reference
---

::component-preview
---
name: SwitchDemo
description: A switch component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add switch
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/switch) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Switch } from '@/components/ui/switch'
</script>

<template>
  <Switch />
</template>
```

## Checked State

Use `default-value` for uncontrolled switches, or `v-model` to control the state.

```vue showLineNumbers
<script setup lang="ts">
import { ref } from 'vue'
import { Switch } from '@/components/ui/switch'

const checked = ref<boolean>(false)
</script>

<template>
  <Switch v-model="checked" />
</template>
```

---

### Apps V4 Content Docs Components Table (`apps/v4/content/docs/components/table.md`)

---
title: Table
description: A responsive table component.
component: true
---

::component-preview
---
name: TableDemo
description: A simple table with invoices.
align: start
previewClass: h-[30rem]
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add table
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/table) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableFooter,
  TableHead,
  TableHeader,
  TableRow,
} from '@/components/ui/table'
</script>

<template>
  <Table>
    <TableCaption>A list of your recent invoices.</TableCaption>
    <TableHeader>
      <TableRow>
        <TableHead class="w-[100px]">
          Invoice
        </TableHead>
        <TableHead>Status</TableHead>
        <TableHead>Method</TableHead>
        <TableHead class="text-right">
          Amount
        </TableHead>
      </TableRow>
    </TableHeader>
    <TableBody>
      <TableRow>
        <TableCell class="font-medium">
          INV001
        </TableCell>
        <TableCell>Paid</TableCell>
        <TableCell>Credit Card</TableCell>
        <TableCell class="text-right">
          $250.00
        </TableCell>
      </TableRow>
    </TableBody>
  </Table>
</template>
```

---

### Apps V4 Content Docs Components Tabs (`apps/v4/content/docs/components/tabs.md`)

---
title: Tabs
description: A set of layered sections of content—known as tab panels—that are displayed one at a time.
component: true
links:
  doc: https://reka-ui.com/docs/components/tabs
  api: https://reka-ui.com/docs/components/tabs#api-reference
---

::component-preview
---
name: TabsDemo
description: A tabs component with account and password tabs.
previewClass: h-[32rem] sm:h-[30rem]
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add tabs
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/tabs) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
</script>

<template>
  <Tabs default-value="account">
    <TabsList>
      <TabsTrigger value="account">
        Account
      </TabsTrigger>
      <TabsTrigger value="password">
        Password
      </TabsTrigger>
    </TabsList>
    <TabsContent value="account">
      Make changes to your account here.
    </TabsContent>
    <TabsContent value="password">
      Change your password here.
    </TabsContent>
  </Tabs>
</template>
```

---

### Apps V4 Content Docs Components Tags Input (`apps/v4/content/docs/components/tags-input.md`)

---
title: Tags Input
description: Tag inputs render tags inside an input, followed by an actual text input.
component: true
links:
  doc: https://reka-ui.com/docs/components/tags-input
  api: https://reka-ui.com/docs/components/tags-input#api-reference
---

::component-preview
---
name: TagsInputDemo
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add tags-input
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/tags-input) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Examples

### Tags with Listbox

::component-preview
---
name: TagsInputWithListbox
---
::

---

### Apps V4 Content Docs Components Textarea (`apps/v4/content/docs/components/textarea.md`)

---
title: Textarea
description: Displays a form textarea or a component that looks like a textarea.
component: true
---

::component-preview
---
name: TextareaDemo
description: A textarea component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add textarea
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/textarea) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Textarea } from '@/components/ui/textarea'
</script>

<template>
  <Textarea placeholder="Type your message here." />
</template>
```

---

### Apps V4 Content Docs Components Toast (`apps/v4/content/docs/components/toast.md`)

---
title: Toast
description: A succinct message that is displayed temporarily.
component: true
links:
  doc: https://reka-ui.com/docs/components/toast
  api: https://reka-ui.com/docs/components/toast#api-reference
---

<Callout title="The toast component has been deprecated." class="mt-0">

  See the [sonner](/docs/components/sonner) documentation for more information.

</Callout>

If you're looking for the old toast component, see the [old docs](https://v3.shadcn-vue.com/docs/components/toast) for more information.

---

### Apps V4 Content Docs Components Toggle Group (`apps/v4/content/docs/components/toggle-group.md`)

---
title: Toggle Group
description: A set of two-state buttons that can be toggled on or off.
component: true
links:
  doc: https://reka-ui.com/docs/components/toggle-group
  api: https://reka-ui.com/docs/components/toggle-group#api-reference
---

::component-preview
---
name: ToggleGroupDemo
description: A toggle group component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add toggle-group
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/toggle-group) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
</script>

<template>
  <ToggleGroup type="multiple">
    <ToggleGroupItem value="a">
      A
    </ToggleGroupItem>
    <ToggleGroupItem value="b">
      B
    </ToggleGroupItem>
    <ToggleGroupItem value="c">
      C
    </ToggleGroupItem>
  </ToggleGroup>
</template>
```

## Examples

### Default

::component-preview
---
name: ToggleGroupDefaultDemo
---
::

### Outline

::component-preview
---
name: ToggleGroupDemo
---
::

### Single

::component-preview
---
name: ToggleGroupSingleDemo
---
::

### Small

::component-preview
---
name: ToggleGroupSmallDemo
---
::

### Large

::component-preview
---
name: ToggleGroupLargeDemo
---
::

### Disabled

::component-preview
---
name: ToggleGroupDisabledDemo
---
::

---

### Apps V4 Content Docs Components Toggle (`apps/v4/content/docs/components/toggle.md`)

---
title: Toggle
description: A two-state button that can be either on or off.
component: true
links:
  doc: https://reka-ui.com/docs/components/toggle
  api: https://reka-ui.com/docs/components/toggle#api-reference
---

::component-preview
---
name: ToggleDemo
description: A toggle component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add toggle
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/toggle) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import { Toggle } from '@/components/ui/toggle'
</script>

<template>
  <Toggle>Toggle</Toggle>
</template>
```

## Pressed State

Use `default-value` for uncontrolled toggles, or `v-model` to control the state.

```vue showLineNumbers
<script setup lang="ts">
import { ref } from 'vue'
import { Toggle } from '@/components/ui/toggle'

const pressed = ref<boolean>(false)
</script>

<template>
  <Toggle v-model="pressed">
    Toggle
  </Toggle>
</template>
```

## Examples

### Default

::component-preview
---
name: ToggleDemo
---
::

### Outline

::component-preview
---
name: ToggleOutlineDemo
---
::

### With Text

::component-preview
---
name: ToggleTextDemo
---
::

### Small

::component-preview
---
name: ToggleSmallDemo
---
::

### Large

::component-preview
---
name: ToggleLargeDemo
---
::

### Disabled

::component-preview
---
name: ToggleDisabledDemo
---
::

---

### Apps V4 Content Docs Components Tooltip (`apps/v4/content/docs/components/tooltip.md`)

---
title: Tooltip
description: A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.
component: true
links:
  doc: https://reka-ui.com/docs/components/tooltip
  api: https://reka-ui.com/docs/components/tooltip#api-reference
---

::component-preview
---
name: TooltipDemo
description: A tooltip component.
---
::

## Installation

:::::code-tabs

:::tabs-list

  ::tabs-trigger{value="cli"}
  CLI
  ::

  ::tabs-trigger{value="manual"}
  Manual
  ::

:::

::tabs-content{value="cli"}

```bash
npx shadcn-vue@latest add tooltip
```

::

::::tabs-content{value="manual"}
  :::steps
    ::step
    Install the following dependencies:
    ::

    ```bash
    npm install reka-ui
    ```

    ::step
    Copy and paste the [GitHub source code](https://github.com/unovue/shadcn-vue/tree/dev/apps/v4/registry/new-york-v4/ui/tooltip) into your project.
    ::

    ::step
    Update the import paths to match your project setup.
    ::
  :::
::::

:::::

## Usage

```vue showLineNumbers
<script setup lang="ts">
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from '@/components/ui/tooltip'
</script>

<template>
  <TooltipProvider>
    <Tooltip>
      <TooltipTrigger>Hover</TooltipTrigger>
      <TooltipContent>
        <p>Add to library</p>
      </TooltipContent>
    </Tooltip>
  </TooltipProvider>
</template>
```

---

### Apps V4 Content Docs Components Typography (`apps/v4/content/docs/components/typography.md`)

---
title: Typography
description: Styles for headings, paragraphs, lists...etc
component: true
---

We do not ship any typography styles by default. This page is an example of how you can use utility classes to style your text.

::component-preview
---
name: TypographyDemo
description: A collection of typographic elements.
class: "[&_.preview]:!h-auto"
hideCode: true
---
::

## h1

::component-preview
---
name: TypographyH1
---
::

## h2

::component-preview
---
name: TypographyH2
---
::

## h3

::component-preview
---
name: TypographyH3
---
::

## h4

::component-preview
---
name: TypographyH4
---
::

## p

::component-preview
---
name: TypographyP
---
::

## blockquote

::component-preview
---
name: TypographyBlockquote
---
::

## table

::component-preview
---
name: TypographyTable
---
::

## list

::component-preview
---
name: TypographyList
---
::

## Inline code

::component-preview
---
name: TypographyInlineCode
---
::

## Lead

::component-preview
---
name: TypographyLead
---
::

## Large

::component-preview
---
name: TypographyLarge
---
::

## Small

::component-preview
---
name: TypographySmall
---
::

## Muted

::component-preview
---
name: TypographyMuted
---
::

---

### Apps V4 Content Docs Dark Mode 01.Vite (`apps/v4/content/docs/dark-mode/01.vite.md`)

---
title: Vite
description: Adding dark mode to your vite app.
---

## Dark mode

::vue-school-link{lesson="dark-mode-with-shadcn-vue" placement="top"}
Watch a Vue School video about how to enable dark mode with shadcn-vue.
::

<Steps>

### Install Dependencies

```bash
npm install @vueuse/core
```

Optional, to include icons for theme button.
```bash
npm install -D @iconify/vue @iconify-json/radix-icons
```

### Add a mode toggle

Place a mode toggle on your site to toggle between light and dark mode.

We're using [`useColorMode`](https://vueuse.org/core/usecolormode/) from [`@vueuse/core`](https://vueuse.org/core/).
> Reactive color mode (dark / light / customs) with auto data persistence.

```vue title="components/ModeToggle.vue"
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { useColorMode } from '@vueuse/core'
import { Button } from '@/components/ui/button'
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'

// Pass { disableTransition: false } to enable transitions
const mode = useColorMode()
</script>

<template>
  <DropdownMenu>
    <DropdownMenuTrigger as-child>
      <Button variant="outline">
        <Icon icon="radix-icons:moon" class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
        <Icon icon="radix-icons:sun" class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
        <span class="sr-only">Toggle theme</span>
      </Button>
    </DropdownMenuTrigger>
    <DropdownMenuContent align="end">
      <DropdownMenuItem @click="mode = 'light'">
        Light
      </DropdownMenuItem>
      <DropdownMenuItem @click="mode = 'dark'">
        Dark
      </DropdownMenuItem>
      <DropdownMenuItem @click="mode = 'auto'">
        System
      </DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</template>
```

</Steps>

---

### Apps V4 Content Docs Dark Mode 02.Nuxt (`apps/v4/content/docs/dark-mode/02.nuxt.md`)

---
title: Nuxt
description: Adding dark mode to your nuxt app.
---

## Dark mode

<Steps>

### Install Dependencies

```bash
npm install -D @nuxtjs/color-mode
```

Then, add `@nuxtjs/color-mode` to the modules section of your `nuxt.config.ts`

```ts
export default defineNuxtConfig({
  modules: [
    '...',
    '@nuxtjs/color-mode'
  ],
  colorMode: {
    classSuffix: ''
  }
})
```

### Add a mode toggle

Place a mode toggle on your site to toggle between light and dark mode.

We're using [`useColorMode`](https://color-mode.nuxtjs.org/usage/basic) from [`Nuxt Color Mode`](https://color-mode.nuxtjs.org/).

Optional, to include icons for theme button.
```bash
npm install -D @iconify/vue @iconify-json/radix-icons
```

```vue title="components/ModeToggle.vue"
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { Button } from '@/components/ui/button'
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'

const colorMode = useColorMode()
</script>

<template>
  <DropdownMenu>
    <DropdownMenuTrigger as-child>
      <Button variant="outline">
        <Icon icon="radix-icons:moon" class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
        <Icon icon="radix-icons:sun" class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
        <span class="sr-only">Toggle theme</span>
      </Button>
    </DropdownMenuTrigger>
    <DropdownMenuContent align="end">
      <DropdownMenuItem @click="colorMode.preference = 'light'">
        Light
      </DropdownMenuItem>
      <DropdownMenuItem @click="colorMode.preference = 'dark'">
        Dark
      </DropdownMenuItem>
      <DropdownMenuItem @click="colorMode.preference = 'system'">
        System
      </DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</template>
```

</Steps>

---

### Apps V4 Content Docs Dark Mode 03.Vitepress (`apps/v4/content/docs/dark-mode/03.vitepress.md`)

---
title: Vitepress
description: Adding dark mode to your vitepress app.
---

## Dark mode

<Steps>

### Install Dependencies

```bash
npm install @vueuse/core
```

Optional, to include icons for theme button.
```bash
npm install -D @iconify/vue @iconify-json/radix-icons
```

### Add a mode toggle

Place a mode toggle on your site to toggle between light and dark mode.

We're using [`useToggle`](https://vueuse.org/shared/useToggle/) from [`@vueuse/core`](https://vueuse.org/core/).
> A boolean switcher with utility functions.

```vue title="components/ModeToggle.vue"
<script setup lang="ts">
import { useToggle } from '@vueuse/core'
import { useData } from 'vitepress'
import { Button } from '@/registry/default/ui/button'

const { frontmatter, isDark } = useData()
const toggleDark = useToggle(isDark)
</script>

<template>
  <Button variant="outline">
    <Icon icon="radix-icons:moon" class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
    <Icon icon="radix-icons:sun" class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
    <span class="sr-only">Toggle theme</span>
  </Button>
</template>
```

</Steps>

---

### Apps V4 Content Docs Dark Mode 04.Astro (`apps/v4/content/docs/dark-mode/04.astro.md`)

---
title: Astro
description: Adding dark mode to your astro app.
---

## Dark mode

<Steps>

### Create an inline theme script

```astro title="src/pages/index.astro"
---
import '../styles/globals.css'
---

<script is:inline>
	const getThemePreference = () => {
		if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
			return localStorage.getItem('theme');
		}
		return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
	};
	const isDark = getThemePreference() === 'dark';
	document.documentElement.classList[isDark ? 'add' : 'remove']('dark');

	if (typeof localStorage !== 'undefined') {
		const observer = new MutationObserver(() => {
			const isDark = document.documentElement.classList.contains('dark');
			localStorage.setItem('theme', isDark ? 'dark' : 'light');
		});
		observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
	}
</script>

<html lang="en">
	<body>
      <h1>Astro</h1>
	</body>
</html>
</script>
```

### Install Dependencies

```bash
npm install @vueuse/core
```

Optional, to include icons for theme button.
```bash
npm install -D @iconify/vue @iconify-json/radix-icons
```

### Add a mode toggle

Place a mode toggle on your site to toggle between light and dark mode.

We're using [`useColorMode`](https://vueuse.org/core/usecolormode/) from [`@vueuse/core`](https://vueuse.org/core/).
> Reactive color mode (dark / light / customs) with auto data persistence.

```vue title="components/ModeToggle.vue"
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { useColorMode } from '@vueuse/core'
import { Button } from '@/components/ui/button'
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'

const mode = useColorMode()
</script>

<template>
  <DropdownMenu>
    <DropdownMenuTrigger as-child>
      <Button variant="outline">
        <Icon icon="radix-icons:moon" class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
        <Icon icon="radix-icons:sun" class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
        <span class="sr-only">Toggle theme</span>
      </Button>
    </DropdownMenuTrigger>
    <DropdownMenuContent align="end">
      <DropdownMenuItem @click="mode = 'light'">
        Light
      </DropdownMenuItem>
      <DropdownMenuItem @click="mode = 'dark'">
        Dark
      </DropdownMenuItem>
      <DropdownMenuItem @click="mode = 'auto'">
        System
      </DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</template>
```

### Display the mode toggle

Place a mode toggle on your site to toggle between light and dark mode.

```astro title="src/pages/index.astro"
---
import '../styles/globals.css'
import ModeToggle from '@/components/ModeToggle.vue';
---



<html lang="en">
	<body>
      <h1>Astro</h1>
      <ModeToggle client:load />
	</body>
</html>
```

</Steps>

---

### Apps V4 Content Docs Forms 01.Vee Validate (`apps/v4/content/docs/forms/01.vee-validate.md`)

---
title: VeeValidate
description: Build forms in Vue using VeeValidate and Zod.
links:
  doc: https://vee-validate.logaretm.com/v4
---

In this guide, we will take a look at building forms with VeeValidate. We'll cover building forms with the `<Field />` component, adding schema validation using Zod, error handling, accessibility, and more.

::vue-school-link{class="mt-6" lesson="forms-and-form-validation-with-shadcn-vue" placement="top"}
Watch a Vue School video about building forms and validation with shadcn-vue.
::

## Demo

We are going to build the following form. It has a simple text input and a textarea. On submit, we'll validate the form data and display any errors.

::callout
---
icon: true
---
**Note:** For the purpose of this demo, we have intentionally disabled browser validation to show how schema validation and form errors work in VeeValidate. It is recommended to add basic browser validation in your production code.
::

::component-preview
---
name: VeeValidateDemo
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

## Approach

This form leverages VeeValidate for performant, flexible form handling. We'll build our form using the `<Field />` component, which gives you **complete flexibility over the markup and styling**.

- Uses VeeValidate's `useForm` composable for form state management.
- VeeValidate's `<Field />` component with scoped slots for controlled inputs with validation.
- shadcn-vue `<Field />` components for building accessible forms.
- Client-side validation using Zod with `toTypedSchema`.

## Anatomy

Here's a basic example of a form using VeeValidate's `<Field />` component with scoped slots and shadcn-vue `<Field />` components.

Bind `componentField` when the control is a Vue component with `v-model`, such as shadcn-vue's `<Input />`. Bind `field` when the control is a native element. `field` binds `value`, which a `v-model` component ignores, so `initialValues` never render.

<br />

::::tabs{default-value="component"}

  :::tabs-list

    ::tabs-trigger{value="component"}
    Component
    ::

    ::tabs-trigger{value="native"}
    Native
    ::

  :::

  ::tabs-content{value="component"}

  #### `Input` Component

  ```vue showLineNumbers {2,9}
  <template>
    <VeeField v-slot="{ componentField, errors }" name="title">
      <Field :data-invalid="!!errors.length">
        <FieldLabel for="title">
          Bug Title
        </FieldLabel>
        <Input
          id="title"
          v-bind="componentField"
          placeholder="Login button not working on mobile"
          autocomplete="off"
          :aria-invalid="!!errors.length"
        />
        <FieldDescription>
          Provide a concise title for your bug report.
        </FieldDescription>
        <FieldError v-if="errors.length" :errors="errors" />
      </Field>
    </VeeField>
  </template>
  ```

  ::

  ::tabs-content{value="native"}

  #### native `input` element

  ```vue showLineNumbers {2,9}
  <template>
    <VeeField v-slot="{ field, errors }" name="title">
      <Field :data-invalid="!!errors.length">
        <FieldLabel for="title">
          Bug Title
        </FieldLabel>
        <input
          id="title"
          v-bind="field"
          placeholder="Login button not working on mobile"
          autocomplete="off"
          :aria-invalid="!!errors.length"
        >
        <FieldDescription>
          Provide a concise title for your bug report.
        </FieldDescription>
        <FieldError v-if="errors.length" :errors="errors" />
      </Field>
    </VeeField>
  </template>
  ```

  ::
::::

## Form

### Create a form schema

We'll start by defining the shape of our form using a Zod schema

::callout
---
icon: true
---
**Note:** This example uses `zod v3` for schema validation, but you can replace it with any other Standard Schema validation library supported by VeeValidate.
::

```vue showLineNumbers title="Form.vue"
<script setup lang="ts">
import * as z from 'zod'

const formSchema = z.object({
  title: z
    .string()
    .min(5, 'Bug title must be at least 5 characters.')
    .max(32, 'Bug title must be at most 32 characters.'),
  description: z
    .string()
    .min(20, 'Description must be at least 20 characters.')
    .max(100, 'Description must be at most 100 characters.'),
})
</script>
```

### Setup the form

Next, we'll use the `useForm` composable from VeeValidate to create our form instance. We'll also add the Zod schema for validation.

```vue showLineNumbers title="Form.vue" {17-23}
<script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod'
import { useForm, Field as VeeField } from 'vee-validate'
import * as z from 'zod'

const formSchema = z.object({
  title: z
    .string()
    .min(5, 'Bug title must be at least 5 characters.')
    .max(32, 'Bug title must be at most 32 characters.'),
  description: z
    .string()
    .min(20, 'Description must be at least 20 characters.')
    .max(100, 'Description must be at most 100 characters.'),
})

const { handleSubmit } = useForm({
  validationSchema: toTypedSchema(formSchema),
  initialValues: {
    title: '',
    description: '',
  },
})

const onSubmit = handleSubmit((values) => {
  // Do something with the form values.
  console.log(values)
})
</script>

<template>
  <form @submit="onSubmit">
    
  </form>
</template>
```

### Build the form

We can now build the form using VeeValidate's `<Field />` component with scoped slots and shadcn-vue `<Field />` components.


::component-source{name="VeeValidateDemo" title="Form.vue"}
::

### Done

That's it. You now have a fully accessible form with client-side validation.

When you submit the form, the `onSubmit` function will be called with the validated form data. If the form data is invalid, VeeValidate will display the errors next to each field.

## Validation

### Client-side Validation

VeeValidate validates your form data using the Zod schema. Define a schema and pass it to the `validationSchema` option of the `useForm` composable.

```vue showLineNumbers title="ExampleForm.vue" {6-9,12}
<script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod'
import { useForm, Field as VeeField } from 'vee-validate'
import * as z from 'zod'

const formSchema = z.object({
  title: z.string(),
  description: z.string().optional(),
})

const { handleSubmit } = useForm({
  validationSchema: toTypedSchema(formSchema),
  initialValues: {
    title: '',
    description: '',
  },
})
</script>
```

### Validation Modes

VeeValidate supports different validation strategies through the `Field` component props.

```vue showLineNumbers title="Form.vue" {4}
<VeeField
  v-slot="{ componentField, errors }"
  name="title"
  :validate-on-input="true"
>
  
</VeeField>
```

| Prop                    | Description                                              |
| ----------------------- | -------------------------------------------------------- |
| `validateOnInput`       | Validation triggers on input event.                     |
| `validateOnChange`      | Validation triggers on change event.                    |
| `validateOnBlur`        | Validation triggers on blur event.                      |
| `validateOnMount`       | Validation triggers when component is mounted.          |

## Displaying Errors

Display errors next to the field using `<FieldError />`. For styling and accessibility:

- Add the `:data-invalid` prop to the shadcn-vue `<Field />` component.
- Add the `:aria-invalid` prop to the form control such as `<Input />`, `<SelectTrigger />`, `<Checkbox />`, etc.

```vue showLineNumbers title="Form.vue" {2,9,11}
<template>
  <VeeField v-slot="{ componentField, errors }" name="email">
    <Field :data-invalid="!!errors.length">
      <FieldLabel for="email">
        Email
      </FieldLabel>
      <Input
        id="email"
        v-bind="componentField"
        type="email"
        :aria-invalid="!!errors.length"
      />
      <FieldError v-if="errors.length" :errors="errors" />
    </Field>
  </VeeField>
</template>
```

## Working with Different Field Types

### Input

- For input fields, use `v-bind="componentField"` to bind VeeValidate's field object to the input.
- To show errors, add the `:aria-invalid` prop to the `<Input />` component and the `:data-invalid` prop to the shadcn-vue `<Field />` component.

::component-preview
---
name: VeeValidateInputDemo
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

For simple text inputs, use VeeValidate's `Field` component with scoped slots.

```vue showLineNumbers title="Form.vue" {3,9,11}
<template>
  <VeeField v-slot="{ componentField, errors }" name="name">
    <Field :data-invalid="!!errors.length">
      <FieldLabel for="name">
        Name
      </FieldLabel>
      <Input
        id="name"
        v-bind="componentField"
        placeholder="Enter your name"
        :aria-invalid="!!errors.length"
      />
      <FieldError v-if="errors.length" :errors="errors" />
    </Field>
  </VeeField>
</template>
```

### Textarea

- For textarea fields, use `v-bind="componentField"` to bind VeeValidate's field object to the textarea.
- To show errors, add the `:aria-invalid` prop to the `<Textarea />` component and the `:data-invalid` prop to the shadcn-vue `<Field />` component.

::component-preview
---
name: VeeValidateTextareaDemo
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

For textarea fields, use VeeValidate's `Field` component with scoped slots.

```vue showLineNumbers title="Form.vue" {3,9,12}
<template>
  <VeeField v-slot="{ componentField, errors }" name="about">
    <Field :data-invalid="!!errors.length">
      <FieldLabel for="about">
        More about you
      </FieldLabel>
      <Textarea
        id="about"
        v-bind="componentField"
        placeholder="I'm a software engineer..."
        class="min-h-[120px]"
        :aria-invalid="!!errors.length"
      />
      <FieldDescription>
        Tell us more about yourself. This will be used to help us personalize your experience.
      </FieldDescription>
      <FieldError v-if="errors.length" :errors="errors" />
    </Field>
  </VeeField>
</template>
```

### Select

- For select components, use `v-bind="componentField"` on `<Select />`. It binds `modelValue`, `update:modelValue` and `name` in one go.
- To show errors, add the `:aria-invalid` prop to the `<SelectTrigger />` component and the `:data-invalid` prop to the shadcn-vue `<Field />` component.

::component-preview
---
name: VeeValidateSelectDemo
class: sm:[&_.preview]:h-[500px] sm:[&_pre]:!h-[500px]
chromeLessOnMobile: true
---
::

```vue showLineNumbers title="Form.vue" {2,11,15}
<template>
  <VeeField v-slot="{ componentField, errors }" name="language">
    <Field orientation="responsive" :data-invalid="!!errors.length">
      <FieldContent>
        <FieldLabel for="language">
          Spoken Language
        </FieldLabel>
        <FieldDescription>For best results, select the language you speak.</FieldDescription>
        <FieldError v-if="errors.length" :errors="errors" />
      </FieldContent>
      <Select v-bind="componentField">
        <SelectTrigger
          id="language"
          class="min-w-[120px]"
          :aria-invalid="!!errors.length"
        >
          <SelectValue placeholder="Select" />
        </SelectTrigger>
        <SelectContent position="item-aligned">
          <SelectItem value="auto">
            Auto
          </SelectItem>
          <SelectItem value="en">
            English
          </SelectItem>
        </SelectContent>
      </Select>
    </Field>
  </VeeField>
</template>
```

### Checkbox

- For a single boolean checkbox, use `v-bind="componentField"` and set `type="checkbox"` on VeeValidate's `<Field />`.
- For checkbox arrays, one field holds the whole array, so bind the `value` and `handleChange` slot props by hand instead.
- To show errors, add the `:aria-invalid` prop to the `<Checkbox />` component and the `:data-invalid` prop to the shadcn-vue `<Field />` component.
- Remember to add `data-slot="checkbox-group"` to the `<FieldGroup />` component for proper styling and spacing.

::component-preview
---
name: VeeValidateCheckboxDemo
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

```vue showLineNumbers title="Form.vue" {2,12}
<template>
  <VeeField v-slot="{ componentField, errors }" name="responses" type="checkbox">
    <FieldSet :data-invalid="!!errors.length">
      <FieldLegend variant="label">
        Responses
      </FieldLegend>
      <FieldDescription>Get notified for requests that take time.</FieldDescription>
      <FieldGroup data-slot="checkbox-group">
        <Field orientation="horizontal">
          <Checkbox
            id="responses"
            v-bind="componentField"
            :aria-invalid="!!errors.length"
          />
          <FieldLabel for="responses" class="font-normal">
            Push notifications
          </FieldLabel>
        </Field>
      </FieldGroup>
      <FieldError v-if="errors.length" :errors="errors" />
    </FieldSet>
  </VeeField>
</template>
```

For an array field, read the current selection from `value` and write the next one with `handleChange`.

```vue showLineNumbers title="Form.vue" {2,17,19-23}
<template>
  <VeeField v-slot="{ value, handleChange, errors }" name="tasks">
    <FieldSet :data-invalid="!!errors.length">
      <FieldLegend variant="label">
        Tasks
      </FieldLegend>
      <FieldDescription>Get notified when tasks you've created have updates.</FieldDescription>
      <FieldGroup data-slot="checkbox-group">
        <Field
          v-for="task in tasks"
          :key="task.id"
          orientation="horizontal"
          :data-invalid="!!errors.length"
        >
          <Checkbox
            :id="`task-${task.id}`"
            :model-value="value?.includes(task.id)"
            :aria-invalid="!!errors.length"
            @update:model-value="(checked: boolean | 'indeterminate') => {
              handleChange(checked
                ? [...(value || []), task.id]
                : (value || []).filter((id: string) => id !== task.id))
            }"
          />
          <FieldLabel :for="`task-${task.id}`" class="font-normal">
            {{ task.label }}
          </FieldLabel>
        </Field>
      </FieldGroup>
      <FieldError v-if="errors.length" :errors="errors" />
    </FieldSet>
  </VeeField>
</template>
```

### Radio Group

- For radio groups, use `v-bind="componentField"` on `<RadioGroup />`. Each `<RadioGroupItem />` only needs its own `value`.
- To show errors, add the `:aria-invalid` prop to the `<RadioGroup />` component and the `:data-invalid` prop to the shadcn-vue `<Field />` component.

::component-preview
---
name: VeeValidateRadioGroupDemo
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

```vue showLineNumbers title="Form.vue" {2,8-11}
<template>
  <VeeField v-slot="{ componentField, errors }" name="plan">
    <FieldSet :data-invalid="!!errors.length">
      <FieldLegend>Plan</FieldLegend>
      <FieldDescription>
        You can upgrade or downgrade your plan at any time.
      </FieldDescription>
      <RadioGroup
        v-bind="componentField"
        :aria-invalid="!!errors.length"
      >
        <FieldLabel v-for="planOption in plans" :key="planOption.id" :for="`plan-${planOption.id}`">
          <Field orientation="horizontal" :data-invalid="!!errors.length">
            <FieldContent>
              <FieldTitle>{{ planOption.title }}</FieldTitle>
              <FieldDescription>{{ planOption.description }}</FieldDescription>
            </FieldContent>
            <RadioGroupItem
              :id="`plan-${planOption.id}`"
              :value="planOption.id"
            />
          </Field>
        </FieldLabel>
      </RadioGroup>
      <FieldError v-if="errors.length" :errors="errors" />
    </FieldSet>
  </VeeField>
</template>
```

### Switch

- For switches, use `v-bind="componentField"` and set `type="checkbox"` on VeeValidate's `<Field />` so the value is treated as a boolean.
- To show errors, add the `:aria-invalid` prop to the `<Switch />` component and the `:data-invalid` prop to the shadcn-vue `<Field />` component.

::component-preview
---
name: VeeValidateSwitchDemo
class: sm:[&_.preview]:h-[500px] sm:[&_pre]:!h-[500px]
chromeLessOnMobile: true
---
::

```vue showLineNumbers title="Form.vue" {2,15}
<template>
  <VeeField v-slot="{ componentField, errors }" name="twoFactor" type="checkbox">
    <Field orientation="horizontal" :data-invalid="!!errors.length">
      <FieldContent>
        <FieldLabel for="two-factor">
          Multi-factor authentication
        </FieldLabel>
        <FieldDescription>
          Enable multi-factor authentication to secure your account.
        </FieldDescription>
        <FieldError v-if="errors.length" :errors="errors" />
      </FieldContent>
      <Switch
        id="two-factor"
        v-bind="componentField"
        :aria-invalid="!!errors.length"
      />
    </Field>
  </VeeField>
</template>
```

### Complex Forms

Here is an example of a more complex form with multiple fields and validation.

::component-preview
---
name: VeeValidateComplexDemo
class: sm:[&_.preview]:h-[1300px] sm:[&_pre]:!h-[1300px]
chromeLessOnMobile: true
---
::

## Resetting the Form

Use the `resetForm` function returned by `useForm` to reset the form to its initial values.

```vue showLineNumbers
<script setup lang="ts">
const { handleSubmit, resetForm } = useForm({
  validationSchema: formSchema,
  // ...
})
</script>

<template>
  <Button type="button" variant="outline" @click="resetForm">
    Reset
  </Button>
</template>
```

## Array Fields

VeeValidate provides a `FieldArray` component for managing dynamic array fields. This is useful when you need to add or remove fields dynamically.

::component-preview
---
name: VeeValidateArrayDemo
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

### Using FieldArray

Use the `FieldArray` component to manage array fields. It provides `fields`, `push`, and `remove` methods through its slot props.

```vue showLineNumbers title="Form.vue" {6-8}
<script setup lang="ts">
import { FieldArray as VeeFieldArray } from 'vee-validate'
</script>

<template>
  <VeeFieldArray v-slot="{ fields, push, remove }" name="emails">
  
  </VeeFieldArray>
</template>
```

### Array Field Structure

Wrap your array fields in a `<FieldSet />` with a `<FieldLegend />` and `<FieldDescription />`.

```vue showLineNumbers title="Form.vue"
<template>
  <FieldSet class="gap-4">
    <FieldLegend variant="label">
      Email Addresses
    </FieldLegend>
    <FieldDescription>
      Add up to 5 email addresses where we can contact you.
    </FieldDescription>
    <FieldGroup class="gap-4">
      
    </FieldGroup>
  </FieldSet>
</template>
```

### Field Pattern for Array Items

Map over the `fields` array and create fields for each item. **Make sure to use `field.key` as the key**.

```vue showLineNumbers title="Form.vue"
<template>
  <VeeFieldArray v-slot="{ fields, push, remove }" name="emails">
    <VeeField
      v-for="(field, index) in fields"
      :key="field.key"
      v-slot="{ componentField: controllerField, errors }"
      :name="`emails[${index}].address`"
    >
      <Field orientation="horizontal" :data-invalid="!!errors.length">
        <FieldContent class="flex-1">
          <InputGroup>
            <InputGroupInput
              :id="`email-${index}`"
              v-bind="controllerField"
              type="email"
              placeholder="name@example.com"
              autocomplete="email"
              :aria-invalid="!!errors.length"
            />
            
          </InputGroup>
          <FieldError v-if="errors.length" :errors="errors" />
        </FieldContent>
      </Field>
    </VeeField>
  </VeeFieldArray>
</template>
```

### Adding Items

Use the `push` method to add new items to the array.

```vue showLineNumbers title="Form.vue"
<template>
  <Button
    type="button"
    variant="outline"
    size="sm"
    :disabled="fields.length >= 5"
    @click="push({ address: '' })"
  >
    Add Email Address
  </Button>
</template>
```

### Removing Items

Use the `remove` method to remove items from the array. Add the remove button conditionally.

```vue showLineNumbers title="Form.vue"
<template>
  <InputGroupAddon v-if="fields.length > 1" align="inline-end">
    <InputGroupButton
      type="button"
      variant="ghost"
      size="icon-xs"
      :aria-label="`Remove email ${index + 1}`"
      @click="remove(index)"
    >
      <XIcon />
    </InputGroupButton>
  </InputGroupAddon>
</template>
```

### Array Validation

Use Zod's `array` method to validate array fields.

```ts showLineNumbers title="Form.vue"
const formSchema = z.object({
  emails: z
    .array(
      z.object({
        address: z.string().email('Enter a valid email address.'),
      }),
    )
    .min(1, 'Add at least one email address.')
    .max(5, 'You can add up to 5 email addresses.'),
})
```

---

### Apps V4 Content Docs Forms 02.Tanstack Form (`apps/v4/content/docs/forms/02.tanstack-form.md`)

---
title: TanStack Form
description: Build forms in Vue using TanStack Form and Zod.
links:
  doc: https://tanstack.com/form
---

This guide explores how to build forms using TanStack Form. You'll learn to create forms with `<Field />` components, implement schema validation with Zod, handle errors, and ensure accessibility.

## Demo

We'll start by building the following form. It has a simple text input and a textarea. On submit, we'll validate the form data and display any errors.

::callout
---
icon: true
---
**Note:** For the purpose of this demo, we have intentionally disabled browser validation to show how schema validation and form errors work in TanStack Form. It is recommended to add basic browser validation in your production code.
::

::component-preview
---
name: TanStackFormDemo
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

## Approach

This form leverages TanStack Form for powerful, headless form handling. We'll build our form using the `<Field />` components, which give you **complete flexibility over the markup and styling**.

- Uses TanStack Form's `useForm` composable for form state management.
- `form.Field` components with render prop pattern for controlled inputs.
- `<Field />` components for building accessible forms.
- Client-side validation using Zod.
- Real-time validation feedback.

## Anatomy

Here's a basic example of a form using TanStack Form with the `<Field />` component.

```vue showLineNumbers {10-26}
<template>
  <form
    @submit.prevent="form.handleSubmit"
  >
    <FieldGroup>
      <form.Field
        name="title"
        #default="{ field }"
      >
        <Field :data-invalid="isInvalid(field)">
          <FieldLabel :for="field.name">Bug Title</FieldLabel>
          <Input
            :id="field.name"
            :name="field.name"
            :model-value="field.state.value"
            @blur="field.handleBlur"
            @input="field.handleChange($event.target.value)"
            :aria-invalid="isInvalid(field)"
            placeholder="Login button not working on mobile"
            autocomplete="off"
          />
          <FieldDescription>
            Provide a concise title for your bug report.
          </FieldDescription>
          <FieldError v-if="isInvalid(field)" :errors="field.state.meta.errors" />
        </Field>
      </form.Field>
    </FieldGroup>
    <Button type="submit">Submit</Button>
  </form>
</template>
```

## Form

### Create a form schema

We'll start by defining the shape of our form using a Zod schema.

**Note:** This example uses `zod v3` for schema validation. TanStack Form integrates seamlessly with Zod and other Standard Schema validation libraries through its validators API.

```vue showLineNumbers
<script setup lang="ts">
import { z } from 'zod'

const formSchema = z.object({
  title: z
    .string()
    .min(5, 'Bug title must be at least 5 characters.')
    .max(32, 'Bug title must be at most 32 characters.'),
  description: z
    .string()
    .min(20, 'Description must be at least 20 characters.')
    .max(100, 'Description must be at most 100 characters.'),
})
</script>
```

### Setup the form

Use the `useForm` composable from TanStack Form to create your form instance with Zod validation.

```vue showLineNumbers
<script setup lang="ts">
import { useForm } from '@tanstack/vue-form'
import { toast } from 'vue-sonner'
import { z } from 'zod'

const formSchema = z.object({
  // ...
})

const form = useForm({
  defaultValues: {
    title: '',
    description: '',
  },
  validators: {
    onSubmit: formSchema,
  },
  onSubmit: async ({ value }) => {
    toast.success('Form submitted successfully')
  },
})

function isInvalid(field) {
  return field.state.meta.isTouched && !field.state.meta.isValid
}
</script>

<template>
  <form @submit.prevent="form.handleSubmit">
    
  </form>
</template>
```

We are using `onSubmit` to validate the form data here. TanStack Form supports other validation modes, which you can read about in the documentation.

### Build the form

We can now build the form using the `form.Field` component from TanStack Form and the `Field` components.

::component-source
---
name: TanStackFormDemo
title: Form.vue
---
::

### Done

That's it. You now have a fully accessible form with client-side validation.

When you submit the form, the `onSubmit` function will be called with the validated form data. If the form data is invalid, TanStack Form will display the errors next to each field.

## Validation

### Client-side Validation

TanStack Form validates your form data using the Zod schema. Validation happens in real-time as the user types.

```vue showLineNumbers
<script setup lang="ts">
import { useForm } from '@tanstack/vue-form'

const formSchema = z.object({
  // ...
})

const form = useForm({
  defaultValues: {
    title: '',
    description: '',
  },
  validators: {
    onSubmit: formSchema,
  },
  onSubmit: async ({ value }) => {
    console.log(value)
  },
})
</script>
```

### Validation Modes

TanStack Form supports different validation strategies through the `validators` option:

| Mode       | Description                           |
| ---------- | ------------------------------------- |
| `onChange` | Validation triggers on every change. |
| `onBlur`   | Validation triggers on blur.         |
| `onSubmit` | Validation triggers on submit.       |

```vue showLineNumbers {7-11}
<script setup lang="ts">
const form = useForm({
  defaultValues: {
    title: '',
    description: '',
  },
  validators: {
    onSubmit: formSchema,
    onChange: formSchema,
    onBlur: formSchema,
  },
})
</script>
```

## Displaying Errors

Display errors next to the field using `FieldError`. For styling and accessibility:
- Add the `:data-invalid` prop to the `Field` component.
- Add the `:aria-invalid` prop to the form control such as `Input`, `SelectTrigger`, `Checkbox`, etc.

```vue showLineNumbers
<script setup lang="ts">
function isInvalid(field) {
  return field.state.meta.isTouched && !field.state.meta.isValid
}
</script>

<template>
  <form.Field
    name="email"
    #default="{ field }"
  >
    <Field :data-invalid="isInvalid(field)">
      <FieldLabel :for="field.name">Email</FieldLabel>
      <Input
        :id="field.name"
        :name="field.name"
        :model-value="field.state.value"
        @blur="field.handleBlur"
        @input="field.handleChange($event.target.value)"
        type="email"
        :aria-invalid="isInvalid(field)"
      />
      <FieldError v-if="isInvalid(field)" :errors="field.state.meta.errors" />
    </Field>
  </form.Field>
</template>
```

## Working with Different Field Types

### Input

For input fields, use `field.state.value` and `field.handleChange` on the `Input` component.
To show errors, add the `:aria-invalid` prop to the `Input` component and the `:data-invalid` prop to the `Field` component.

::component-preview
---
name: TanStackFormInput
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

```vue showLineNumbers {6, 11-14, 22}
<template>
  <form.Field
    name="username"
    #default="{ field }"
  >
    <Field :data-invalid="isInvalid(field)">
      <FieldLabel :for="`form-tanstack-input-username`">Username</FieldLabel>
      <Input
        id="form-tanstack-input-username"
        :name="field.name"
        :model-value="field.state.value"
        @blur="field.handleBlur"
        @input="field.handleChange($event.target.value)"
        :aria-invalid="isInvalid(field)"
        placeholder="shadcn"
        autocomplete="username"
      />
      <FieldDescription>
        This is your public display name. Must be between 3 and 10 characters.
        Must only contain letters, numbers, and underscores.
      </FieldDescription>
      <FieldError v-if="isInvalid(field)" :errors="field.state.meta.errors" />
    </Field>
  </form.Field>
</template>
```

### Textarea

For textarea fields, use `field.state.value` and `field.handleChange` on the `Textarea` component.
To show errors, add the `:aria-invalid` prop to the `Textarea` component and the `:data-invalid` prop to the `Field` component.

::component-preview
---
name: TanStackFormTextarea
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

```vue showLineNumbers {6,13-16,24}
<template>
  <form.Field
    name="about"
    #default="{ field }"
  >
    <Field :data-invalid="isInvalid(field)">
      <FieldLabel :for="`form-tanstack-textarea-about`">
        More about you
      </FieldLabel>
      <Textarea
        id="form-tanstack-textarea-about"
        :name="field.name"
        :model-value="field.state.value"
        @blur="field.handleBlur"
        @input="field.handleChange($event.target.value)"
        :aria-invalid="isInvalid(field)"
        placeholder="I'm a software engineer..."
        class="min-h-[120px]"
      />
      <FieldDescription>
        Tell us more about yourself. This will be used to help us
        personalize your experience.
      </FieldDescription>
      <FieldError v-if="isInvalid(field)" :errors="field.state.meta.errors" />
    </Field>
  </form.Field>
</template>
```

### Select

For select components, use `field.state.value` and `field.handleChange` on the `Select` component.
To show errors, add the `:aria-invalid` prop to the `SelectTrigger` component and the `:data-invalid` prop to the `Field` component.

::component-preview
---
name: TanStackFormSelect
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

```vue showLineNumbers {6, 14, 18-19, 23}
<template>
  <form.Field
    name="language"
    #default="{ field }"
  >
    <Field orientation="responsive" :data-invalid="isInvalid(field)">
      <FieldContent>
        <FieldLabel :for="`form-tanstack-select-language`">
          Spoken Language
        </FieldLabel>
        <FieldDescription>
          For best results, select the language you speak.
        </FieldDescription>
        <FieldError v-if="isInvalid(field)" :errors="field.state.meta.errors" />
      </FieldContent>
      <Select
        :name="field.name"
        :model-value="field.state.value"
        @update:model-value="field.handleChange"
      >
        <SelectTrigger
          id="form-tanstack-select-language"
          :aria-invalid="isInvalid(field)"
          class="min-w-[120px]"
        >
          <SelectValue placeholder="Select" />
        </SelectTrigger>
        <SelectContent position="item-aligned">
          <SelectItem value="auto">Auto</SelectItem>
          <SelectSeparator />
          <SelectItem
            v-for="language in spokenLanguages"
            :key="language.value"
            :value="language.value"
          >
            {{ language.label }}
          </SelectItem>
        </SelectContent>
      </Select>
    </Field>
  </form.Field>
</template>
```

### Checkbox

For checkboxes, use `field.state.value` and `field.handleChange` on the `Checkbox` component.
To show errors, add the `:aria-invalid` prop to the `Checkbox` component and the `:data-invalid` prop to the `Field` component.
For checkbox arrays, use `mode="array"` on the `form.Field` component and TanStack Form's array helpers.
Remember to add `data-slot="checkbox-group"` to the `FieldGroup` component for proper styling and spacing.

::component-preview
---
name: TanStackFormCheckbox
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

```vue showLineNumbers {12, 17, 22-33, 43}
<template>
  <form.Field
    name="tasks"
    mode="array"
    #default="{ field }"
  >
    <FieldSet>
      <FieldLegend variant="label">Tasks</FieldLegend>
      <FieldDescription>
        Get notified when tasks you've created have updates.
      </FieldDescription>
      <FieldGroup data-slot="checkbox-group">
        <Field
          v-for="task in tasks"
          :key="task.id"
          orientation="horizontal"
          :data-invalid="isInvalid(field)"
        >
          <Checkbox
            :id="`form-tanstack-checkbox-${task.id}`"
            :name="field.name"
            :aria-invalid="isInvalid(field)"
            :model-value="field.state.value.includes(task.id)"
            @update:model-value="(checked | 'indeterminate') => {
              if (checked) {
                field.pushValue(task.id)
              } else {
                const index = field.state.value.indexOf(task.id)
                if (index > -1) {
                  field.removeValue(index)
                }
              }
            }"
          />
          <FieldLabel
            :for="`form-tanstack-checkbox-${task.id}`"
            class="font-normal"
          >
            {{ task.label }}
          </FieldLabel>
        </Field>
      </FieldGroup>
      <FieldError v-if="isInvalid(field)" :errors="field.state.meta.errors" />
    </FieldSet>
  </form.Field>
</template>
```

### Radio Group

For radio groups, use `field.state.value` and `field.handleChange` on the `RadioGroup` component.
To show errors, add the `:aria-invalid` prop to the `RadioGroupItem` component and the `:data-invalid` prop to the `Field` component.

::component-preview
---
name: TanStackFormRadioGroup
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

```vue showLineNumbers {13-14, 23, 32, 37}
<template>
  <form.Field
    name="plan"
    #default="{ field }"
  >
    <FieldSet>
      <FieldLegend>Plan</FieldLegend>
      <FieldDescription>
        You can upgrade or downgrade your plan at any time.
      </FieldDescription>
      <RadioGroup
        :name="field.name"
        :model-value="field.state.value"
        @update:model-value="field.handleChange"
      >
        <FieldLabel
          v-for="plan in plans"
          :key="plan.id"
          :for="`form-tanstack-radiogroup-${plan.id}`"
        >
          <Field
            orientation="horizontal"
            :data-invalid="isInvalid(field)"
          >
            <FieldContent>
              <FieldTitle>{{ plan.title }}</FieldTitle>
              <FieldDescription>{{ plan.description }}</FieldDescription>
            </FieldContent>
            <RadioGroupItem
              :value="plan.id"
              :id="`form-tanstack-radiogroup-${plan.id}`"
              :aria-invalid="isInvalid(field)"
            />
          </Field>
        </FieldLabel>
      </RadioGroup>
      <FieldError v-if="isInvalid(field)" :errors="field.state.meta.errors" />
    </FieldSet>
  </form.Field>
</template>
```

### Switch

For switches, use `field.state.value` and `field.handleChange` on the `Switch` component.
To show errors, add the `:aria-invalid` prop to the `Switch` component and the `:data-invalid` prop to the `Field` component.

::component-preview
---
name: TanStackFormSwitch
class: sm:[&_.preview]:h-[500px] sm:[&_pre]:!h-[500px]
chromeLessOnMobile: true
---
::

```vue showLineNumbers {6, 14, 19-21}
<template>
  <form.Field
    name="twoFactor"
    #default="{ field }"
  >
    <Field orientation="horizontal" :data-invalid="isInvalid(field)">
      <FieldContent>
        <FieldLabel :for="field.name">
          Multi-factor authentication
        </FieldLabel>
        <FieldDescription>
          Enable multi-factor authentication to secure your account.
        </FieldDescription>
        <FieldError v-if="isInvalid(field)" :errors="field.state.meta.errors" />
      </FieldContent>
      <Switch
        :id="field.name"
        :name="field.name"
        :model-value="field.state.value"
        @update:model-value="field.handleChange"
        :aria-invalid="isInvalid(field)"
      />
    </Field>
  </form.Field>
</template>
```

### Complex Forms

Here is an example of a more complex form with multiple fields and validation.

::component-preview
---
name: TanStackFormComplex
class: sm:[&_.preview]:h-[1100px] sm:[&_pre]:!h-[1100px]
chromeLessOnMobile: true
---
::

## Resetting the Form

Use `form.reset()` to reset the form to its default values.

```vue showLineNumbers
<template>
  <Button type="button" variant="outline" @click="form.reset()">
    Reset
  </Button>
</template>
```

## Array Fields

TanStack Form provides powerful array field management with `mode="array"`. This allows you to dynamically add, remove, and update array items with full validation support.

::component-preview
---
name: TanStackFormArray
class: sm:[&_.preview]:h-[700px] sm:[&_pre]:!h-[700px]
chromeLessOnMobile: true
---
::

This example demonstrates managing multiple email addresses with array fields. Users can add up to 5 email addresses, remove individual addresses, and each address is validated independently.

### Using FieldArray

Use `mode="array"` on the parent field to enable array field management.

```vue showLineNumbers {4, 13-15}
<template>
  <form.Field
    name="emails"
    mode="array"
    #default="{ field }"
  >
    <FieldSet>
      <FieldLegend variant="label">Email Addresses</FieldLegend>
      <FieldDescription>
        Add up to 5 email addresses where we can contact you.
      </FieldDescription>
      <FieldGroup>
        <template v-for="(_, index) in field.state.value">
          
        </template>
      </FieldGroup>
    </FieldSet>
  </form.Field>
</template>
```

### Nested Fields

Access individual array items using bracket notation: `fieldName[index].propertyName`. This example uses `InputGroup` to display the remove button inline with the input.

```vue showLineNumbers
<template>
  <form.Field
    :name="`emails[${index}].address`"
    #default="{ subField }"
  >
    <Field orientation="horizontal" :data-invalid="isSubFieldInvalid(subField)">
      <FieldContent>
        <InputGroup>
          <InputGroupInput
            :id="`form-tanstack-array-email-${index}`"
            :name="subField.name"
            :model-value="subField.state.value"
            @blur="subField.handleBlur"
            @input="subField.handleChange($event.target.value)"
            :aria-invalid="isSubFieldInvalid(subField)"
            placeholder="name@example.com"
            type="email"
          />
          <InputGroupAddon v-if="field.state.value.length > 1" align="inline-end">
            <InputGroupButton
              type="button"
              variant="ghost"
              size="icon-xs"
              @click="field.removeValue(index)"
              :aria-label="`Remove email ${index + 1}`"
            >
              <XIcon />
            </InputGroupButton>
          </InputGroupAddon>
        </InputGroup>
        <FieldError v-if="isSubFieldInvalid(subField)" :errors="subField.state.meta.errors" />
      </FieldContent>
    </Field>
  </form.Field>
</template>
```


### Adding Items

Use `field.pushValue(item)` to add items to an array field. You can disable the button when the array reaches its maximum length.

```vue showLineNumbers
<template>
  <Button
    type="button"
    variant="outline"
    size="sm"
    @click="field.pushValue({ address: '' })"
    :disabled="field.state.value.length >= 5"
  >
    Add Email Address
  </Button>
</template>
```

### Removing Items

Use `field.removeValue(index)` to remove items from an array field. You can conditionally show the remove button only when there's more than one item.

```vue showLineNumbers
<template>
  <InputGroupButton
    v-if="field.state.value.length > 1"
    @click="field.removeValue(index)"
    :aria-label="`Remove email ${index + 1}`"
  >
    <XIcon />
  </InputGroupButton>
</template>
```

### Array Validation

Validate array fields using Zod's array methods.

```vue showLineNumbers
<script setup lang="ts">
const formSchema = z.object({
  emails: z
    .array(
      z.object({
        address: z.string().email('Enter a valid email address.'),
      })
    )
    .min(1, 'Add at least one email address.')
    .max(5, 'You can add up to 5 email addresses.'),
})
</script>
```

---

--- METRICS ---
- Files Extracted: 109
- Estimated Token Budget: ~127773 tokens
- Canonical Reference: https://codewiki.google/github.com/unovue/shadcn-vue
