# Introduction
## What is Nuxt UI?
A modern UI library built on [Reka UI](https://reka-ui.com/){rel="nofollow"}, [Tailwind CSS](https://tailwindcss.com/){rel="nofollow"}, and [Tailwind Variants](https://www.tailwind-variants.org/){rel="nofollow"} to ship beautiful and accessible applications with 100+ production-ready components.
::card-group
:::card{icon="i-lucide-sparkles" title="Developer Experience First"}
Intuitive APIs, excellent TypeScript support, auto-completion, and comprehensive docs.
:::
:::card{icon="i-lucide-palette" title="Beautiful by Default"}
A modern, clean design out of the box with a theme you can adapt in minutes.
:::
:::card{icon="i-lucide-accessibility" title="Accessible by Default"}
WAI-ARIA compliant with keyboard navigation, focus management, and screen reader support.
:::
:::card{icon="i-lucide-blocks" title="Production Ready"}
100+ battle-tested components used by thousands of applications in production.
:::
::
## What's new in v4?
Nuxt UI v4 marks a major milestone: Nuxt UI and Nuxt UI Pro are now unified into a single, fully open-source and free library of 100+ production-ready components and a complete Figma Kit.
The migration from v3 to v4 will be much smoother than from v2 to v3. Read more in the [migration guide](https://ui.nuxt.com/docs/getting-started/migration/v4).
::note{to="https://ui.nuxt.com/docs/getting-started/migration/v3"}
If you are migrating from v2, you can read more in this **migration guide**.
::
## Core technologies
### Reka UI
Nuxt UI is built on top of [Reka UI](https://reka-ui.com/){rel="nofollow"} as a foundation for the components:
- **WAI-ARIA Compliance**: Follows [WAI-ARIA authoring practices](https://reka-ui.com/docs/overview/accessibility){rel="nofollow"} with proper semantics and roles
- **Keyboard Navigation**: Built-in keyboard support for complex components like tabs and dialogs
- **Focus Management**: Intelligent focus handling that moves focus based on user interactions
- **Accessible Labels**: Abstractions to simplify labeling controls for screen readers
### Tailwind CSS
Nuxt UI integrates the latest [Tailwind CSS](https://tailwindcss.com/){rel="nofollow"} v4, bringing significant improvements:
- **5x Faster Builds**: Full builds up to 5x faster, incremental builds over 100x faster
- **Unified Toolchain**: Built-in import handling, vendor prefixing, and syntax transforms
- **CSS-first Configuration**: Customize and extend directly in CSS instead of JavaScript
- **Modern Web Features**: Container queries, cascade layers, wide-gamut colors, and more
### Tailwind Variants
Nuxt UI takes advantage of [Tailwind Variants](https://www.tailwind-variants.org/){rel="nofollow"} to provide a powerful theming system:
- **Dynamic Styling**: Flexible component variants with a powerful API
- **Type Safety**: Full TypeScript support with auto-completion
- **Conflict Resolution**: Efficient merging of conflicting styles
## Key features
### Ecosystem integration
Nuxt UI integrates with the Nuxt ecosystem to provide a seamless development experience:
- [**Icons**](https://ui.nuxt.com/docs/getting-started/integrations/icons): Access 200,000+ icons from Iconify
- [**Fonts**](https://ui.nuxt.com/docs/getting-started/integrations/fonts): Plug-and-play web font optimization and configuration
- [**Color Mode**](https://ui.nuxt.com/docs/getting-started/integrations/color-mode): Dark and Light mode with auto detection
- [**i18n**](https://ui.nuxt.com/docs/getting-started/integrations/i18n): Internationalize your components with 50+ languages
- [**Content**](https://ui.nuxt.com/docs/getting-started/integrations/content): Beautiful typography out of the box
### Vue Compatibility
Nuxt UI works with any Vue project. Simply add the Vite and Vue plugins to your configuration:
- **Auto-imports**: Components and composables are automatically imported and available globally
- **Theming System**: Full theming support with customizable colors, sizes, variants, and more
- **Developer Experience**: Complete TypeScript support with IntelliSense and auto-completion
::tip
---
ariaLabel: Vue installation guide
to: https://ui.nuxt.com/docs/getting-started/installation/vue
---
Learn how to install and configure Nuxt UI in a Vue project in the **Vue installation guide**.
::
### TypeScript Support
Nuxt UI provides comprehensive TypeScript integration for a superior developer experience:
- **Auto-completion**: For all component props, slots, and events
- **Generic Components**: Using [Vue Generics](https://vuejs.org/api/sfc-script-setup.html#generics){rel="nofollow"}
- **Type-safe Theming**: In `app.config.ts`
- **IntelliSense**: Throughout your entire codebase
## FAQ
::accordion
:::accordion-item{label="Is Nuxt UI free to use?"}
Yes! Nuxt UI is completely free and open source under the MIT license. All 100+ components are available to everyone.
:::
:::accordion-item{label="Can I use Nuxt UI with Vue without Nuxt?"}
Yes! While optimized for Nuxt, Nuxt UI works perfectly with standalone Vue projects via our Vite plugin. You can follow the [installation guide](https://ui.nuxt.com/docs/getting-started/installation/vue) to get started.
:::
:::accordion-item
---
label: Will Nuxt UI work with other CSS frameworks like UnoCSS?
---
No. Nuxt UI is designed exclusively for Tailwind CSS. UnoCSS support would require significant architecture changes due to different class naming conventions.
:::
:::accordion-item{label="How does Nuxt UI handle accessibility?"}
Through [Reka UI](https://reka-ui.com/docs/overview/accessibility){rel="nofollow"} integration, Nuxt UI provides automatic ARIA attributes, keyboard navigation, focus management, and screen reader support. While offering a strong foundation, testing in your specific use case remains important.
:::
:::accordion-item{label="How is Nuxt UI tested?"}
Nuxt UI ensures reliability with 1000+ Vitest tests covering core functionality and accessibility.
:::
:::accordion-item{label="Is Nuxt UI production-ready?"}
Yes! Nuxt UI is used in production by thousands of applications with extensive tests, regular updates, and active maintenance.
:::
::
# Installation
::callout
---
class: hidden
icon: i-logos-vue
to: https://ui.nuxt.com/docs/getting-started/installation/vue
---
Looking for the **Vue** version?
::
## Setup
### Add to a Nuxt project
::steps{level="4"}
#### Install the Nuxt UI package
:::code-group{sync="pm"}
```bash [pnpm]
pnpm add @nuxt/ui
```
```bash [yarn]
yarn add @nuxt/ui
```
```bash [npm]
npm install @nuxt/ui
```
```bash [bun]
bun add @nuxt/ui
```
:::
:::warning
If you're using **pnpm**, ensure that you either set [`shamefully-hoist=true`](https://pnpm.io/npmrc#shamefully-hoist){rel="nofollow"} in your `.npmrc` file or install `tailwindcss` in your project's root directory.
:::
#### Add the Nuxt UI module in your `nuxt.config.ts`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui']
})
```
#### Import Tailwind CSS and Nuxt UI in your CSS
:::code-group
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
```
```ts [nuxt.config.ts] {3}
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css']
})
```
:::
:::callout{icon="i-simple-icons-visualstudiocode"}
It's recommended to install the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss){rel="nofollow"} extension for VSCode and add the following settings:
```json [.vscode/settings.json]
{
"files.associations": {
"*.css": "tailwindcss"
},
"editor.quickSuggestions": {
"strings": "on"
},
"tailwindCSS.classAttributes": ["class", "ui"],
"tailwindCSS.experimental.classRegex": [
["ui:\\s*{([^)]*)\\s*}", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
]
}
```
:::
#### Wrap your app with App component
```vue [app.vue]
```
:::note{to="https://ui.nuxt.com/docs/components/app"}
The `App` component provides global configurations and is required for **Toast**, **Tooltip** components to work as well as **Programmatic Overlays**.
:::
::
### Use a Nuxt template
To quickly get started with Nuxt UI, you can use the [starter template](https://github.com/nuxt-ui-templates/starter){rel="nofollow"} by running:
```bash [Terminal]
npm create nuxt@latest -- -t ui
```
You can also get started with one of our [official templates](https://ui.nuxt.com/templates):
::card-group
:::card
---
color: neutral
icon: i-simple-icons-github
target: _blank
title: Starter
to: https://github.com/nuxt-ui-templates/starter
---
A minimal template to get started with Nuxt UI.
:::
:::card
---
color: neutral
icon: i-simple-icons-github
target: _blank
title: Landing
to: https://github.com/nuxt-ui-templates/landing
---
A modern landing page template powered by Nuxt Content.
:::
:::card
---
color: neutral
icon: i-simple-icons-github
target: _blank
title: Docs
to: https://github.com/nuxt-ui-templates/docs
---
A documentation template powered by Nuxt Content.
:::
:::card
---
color: neutral
icon: i-simple-icons-github
target: _blank
title: SaaS
to: https://github.com/nuxt-ui-templates/saas
---
A SaaS template with landing, pricing, docs and blog powered by Nuxt Content.
:::
:::card
---
color: neutral
icon: i-simple-icons-github
target: _blank
title: Dashboard
to: https://github.com/nuxt-ui-templates/dashboard
variant: subtle
---
A dashboard template with multi-column layout for building sophisticated admin interfaces.
:::
:::card
---
color: neutral
icon: i-simple-icons-github
target: _blank
title: Chat
to: https://github.com/nuxt-ui-templates/chat
---
An AI chatbot template to build your own chatbot powered by Nuxt MDC and Vercel AI SDK.
:::
:::card
---
color: neutral
icon: i-simple-icons-github
target: _blank
title: Portfolio
to: https://github.com/nuxt-ui-templates/portfolio
---
A sleek portfolio template to showcase your work, skills and blog powered by Nuxt Content.
:::
:::card
---
color: neutral
icon: i-simple-icons-github
target: _blank
title: Changelog
to: https://github.com/nuxt-ui-templates/changelog
---
A changelog template to display your repository releases notes from GitHub powered by Nuxt MDC.
:::
::
You can use the `Use this template` button on GitHub to create a new repository or use the CLI:
::code-group
```bash [Starter]
npm create nuxt@latest -- -t ui
```
```bash [Landing]
npm create nuxt@latest -- -t ui/landing
```
```bash [Docs]
npm create nuxt@latest -- -t ui/docs
```
```bash [SaaS]
npm create nuxt@latest -- -t ui/saas
```
```bash [Dashboard]
npm create nuxt@latest -- -t ui/dashboard
```
```bash [Chat]
npm create nuxt@latest -- -t ui/chat
```
```bash [Portfolio]
npm create nuxt@latest -- -t ui/portfolio
```
```bash [Changelog]
npm create nuxt@latest -- -t ui/changelog
```
::
## Options
You can customize Nuxt UI by providing options in your `nuxt.config.ts`.
### `prefix`
Use the `prefix` option to change the prefix of the components.
- Default: `U`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
prefix: 'Nuxt'
}
})
```
### `fonts`
Use the `fonts` option to enable or disable the [`@nuxt/fonts`](https://github.com/nuxt/fonts){rel="nofollow"} module.
- Default: `true`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
fonts: false
}
})
```
### `colorMode`
Use the `colorMode` option to enable or disable the [`@nuxt/color-mode`](https://github.com/nuxt-modules/color-mode){rel="nofollow"} module.
- Default: `true`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
colorMode: false
}
})
```
### `theme.colors`
Use the `theme.colors` option to define the dynamic color aliases used to generate components theme.
- Default: `['primary', 'secondary', 'success', 'info', 'warning', 'error']`{.inline,language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
theme: {
colors: ['primary', 'error']
}
}
})
```
::tip{to="https://ui.nuxt.com/docs/getting-started/theme/design-system#colors"}
Learn more about color customization and theming in the Theme section.
::
### `theme.transitions`
Use the `theme.transitions` option to enable or disable transitions on components.
- Default: `true`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
theme: {
transitions: false
}
}
})
```
::note
This option adds the `transition-colors` class on components with hover or active states.
::
### `theme.defaultVariants`
Use the `theme.defaultVariants` option to override the default `color` and `size` variants for components.
- Default: `{ color: 'primary', size: 'md' }`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
theme: {
defaultVariants: {
color: 'neutral',
size: 'sm'
}
}
}
})
```
### `mdc`
Use the `mdc` option to force the import of Nuxt UI `` components even if `@nuxtjs/mdc` or `@nuxt/content` is not installed.
- Default: `false`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
mdc: true
}
})
```
### `content`
Use the `content` option to force the import of Nuxt UI `` and `` components even if `@nuxt/content` is not installed (`@nuxtjs/mdc` is installed by `@nuxt/content`).
- Default: `false`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
content: true
}
})
```
### `experimental.componentDetection` :badge{label="4.1+"}
Use the `experimental.componentDetection` option to enable automatic component detection for tree-shaking. This feature scans your source code to detect which components are actually used and only generates the necessary CSS for those components (including their dependencies).
- Default: `false`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- Type: `boolean | string[]`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
**Enable automatic detection:**
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
experimental: {
componentDetection: true
}
}
})
```
**Include additional components for dynamic usage:**
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
experimental: {
componentDetection: ['Modal', 'Dropdown', 'Popover']
}
}
})
```
::note
When providing an array of component names, automatic detection is enabled and these components (along with their dependencies) are guaranteed to be included. This is useful for dynamic components like `` that can't be statically analyzed.
::
## Continuous releases
Nuxt UI uses [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new){rel="nofollow"} for continuous preview releases, providing developers with instant access to the latest features and bug fixes without waiting for official releases.
Automatic preview releases are created for all commits and PRs to the `v4` branch. Use them by replacing your package version with the specific commit hash or PR number.
```diff [package.json]
{
"dependencies": {
- "@nuxt/ui": "^4.0.0",
+ "@nuxt/ui": "https://pkg.pr.new/@nuxt/ui@4c96909",
}
}
```
::note
**pkg.pr.new** will automatically comment on PRs with the installation URL, making it easy to test changes.
::
# Installation
::callout
---
class: hidden
icon: i-logos-nuxt-icon
to: https://ui.nuxt.com/docs/getting-started/installation/nuxt
---
Looking for the **Nuxt** version?
::
## Setup
### Add to a Vue project
::steps{level="4"}
#### Install the Nuxt UI package
:::code-group{sync="pm"}
```bash [pnpm]
pnpm add @nuxt/ui
```
```bash [yarn]
yarn add @nuxt/ui
```
```bash [npm]
npm install @nuxt/ui
```
```bash [bun]
bun add @nuxt/ui
```
:::
:::warning
If you're using **pnpm**, ensure that you either set [`shamefully-hoist=true`](https://pnpm.io/npmrc#shamefully-hoist){rel="nofollow"} in your `.npmrc` file or install `tailwindcss`, `vue-router` and `@unhead/vue` in your project's root directory.
:::
#### Add the Nuxt UI Vite plugin in your `vite.config.ts`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
:::code-group{sync="vite"}
```ts [vite.config.ts (Vite)] {3,8}
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui()
]
})
```
```ts [vite.config.ts (Laravel Inertia)] {3,20-22}
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
import laravel from 'laravel-vite-plugin'
export default defineConfig({
plugins: [
laravel({
input: ['resources/js/app.ts'],
refresh: true
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false
}
}
}),
ui({
inertia: true
})
]
})
```
```ts [vite.config.ts (AdonisJS Inertia)] {3,15-17}
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
import adonisjs from '@adonisjs/vite/client'
import inertia from '@adonisjs/inertia/client'
export default defineConfig({
plugins: [
adonisjs({
entrypoints: ['inertia/app/app.ts'],
reload: ['resources/views/**/*.edge']
}),
inertia(),
vue(),
ui({
inertia: true
})
]
})
```
:::
:::tip
Nuxt UI registers `unplugin-auto-import` and `unplugin-vue-components`, which will generate `auto-imports.d.ts` and `components.d.ts` type declaration files. You will likely want to gitignore these, and add them to your `tsconfig`.
```json [tsconfig.app.json]
{
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "auto-imports.d.ts", "components.d.ts"]
}
```
```bash [.gitignore]
# Auto-generated type declarations
auto-imports.d.ts
components.d.ts
```
:::
:::tip
Internally, Nuxt UI relies on custom alias to resolve the theme types. If you're using TypeScript, you should add an alias to your `tsconfig` to enable auto-completion in your `vite.config.ts`.
```json [tsconfig.node.json]
{
"compilerOptions": {
"paths": {
"#build/ui": [
"./node_modules/.nuxt-ui/ui"
]
}
}
}
```
:::
#### Use the Nuxt UI Vue plugin
:::code-group{sync="vite"}
```ts [src/main.ts (Vite)] {3,14}
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import ui from '@nuxt/ui/vue-plugin'
import App from './App.vue'
const app = createApp(App)
const router = createRouter({
routes: [],
history: createWebHistory()
})
app.use(router)
app.use(ui)
app.mount('#app')
```
```ts [resources/js/app.ts (Laravel Inertia)] {3,19}
import type { DefineComponent } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
import ui from '@nuxt/ui/vue-plugin'
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'
import { createApp, h } from 'vue'
const appName = import.meta.env.VITE_APP_NAME || 'Laravel x Nuxt UI'
createInertiaApp({
title: title => (title ? `${title} - ${appName}` : appName),
resolve: name =>
resolvePageComponent(
`./pages/${name}.vue`,
import.meta.glob('./pages/**/*.vue')
),
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.use(ui)
.mount(el)
}
})
```
```ts [inertia/app/app.ts (AdonisJS Inertia)] {3,19}
import type { DefineComponent } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
import ui from '@nuxt/ui/vue-plugin'
import { resolvePageComponent } from '@adonisjs/inertia/helpers'
import { createApp, h } from 'vue'
const appName = import.meta.env.VITE_APP_NAME || 'AdonisJS x Nuxt UI'
createInertiaApp({
title: title => (title ? `${title} - ${appName}` : appName),
resolve: name =>
resolvePageComponent(
`../pages/${name}.vue`,
import.meta.glob('../pages/**/*.vue')
),
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.use(ui)
.mount(el)
}
})
```
:::
#### Import Tailwind CSS and Nuxt UI in your CSS
:::code-group{sync="vite"}
```css [assets/main.css (Vite)]
@import "tailwindcss";
@import "@nuxt/ui";
```
```css [resources/css/app.css (Laravel Inertia)]
@import "tailwindcss";
@import "@nuxt/ui";
```
```css [inertia/css/app.css (AdonisJS Inertia)]
@import "tailwindcss";
@import "@nuxt/ui";
```
:::
:::tip
Import the CSS file in your entrypoint.
::::code-group{sync="vite"}
```ts [src/main.ts] {1}
import './assets/main.css'
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import ui from '@nuxt/ui/vue-plugin'
import App from './App.vue'
const app = createApp(App)
const router = createRouter({
routes: [],
history: createWebHistory()
})
app.use(router)
app.use(ui)
app.mount('#app')
```
```ts [resources/js/app.ts (Laravel Inertia)] {1}
import '../css/app.css'
import type { DefineComponent } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
import ui from '@nuxt/ui/vue-plugin'
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'
import { createApp, h } from 'vue'
const appName = import.meta.env.VITE_APP_NAME || 'Laravel x Nuxt UI'
createInertiaApp({
title: title => (title ? `${title} - ${appName}` : appName),
resolve: name =>
resolvePageComponent(
`./pages/${name}.vue`,
import.meta.glob('./pages/**/*.vue')
),
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.use(ui)
.mount(el)
}
})
```
```ts [inertia/app/app.ts (AdonisJS Inertia)] {1}
import '../css/app.css'
import type { DefineComponent } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
import ui from '@nuxt/ui/vue-plugin'
import { resolvePageComponent } from '@adonisjs/inertia/helpers'
import { createApp, h } from 'vue'
const appName = import.meta.env.VITE_APP_NAME || 'AdonisJS x Nuxt UI'
createInertiaApp({
title: title => (title ? `${title} - ${appName}` : appName),
resolve: name =>
resolvePageComponent(
`../pages/${name}.vue`,
import.meta.glob('../pages/**/*.vue')
),
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.use(ui)
.mount(el)
}
})
```
::::
:::
:::callout{icon="i-simple-icons-visualstudiocode"}
It's recommended to install the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss){rel="nofollow"} extension for VSCode and add the following settings:
```json [.vscode/settings.json]
{
"files.associations": {
"*.css": "tailwindcss"
},
"editor.quickSuggestions": {
"strings": "on"
},
"tailwindCSS.classAttributes": ["class", "ui"],
"tailwindCSS.experimental.classRegex": [
["ui:\\s*{([^)]*)\\s*}", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
]
}
```
:::
#### Wrap your app with App component
:::code-group{sync="vite"}
```vue [src/App.vue (Vite)]
```
```vue [resources/js/pages/index.vue (Laravel Inertia)]
```
```vue [inertia/pages/index.vue (AdonisJS Inertia)]
```
:::
:::note{to="https://ui.nuxt.com/docs/components/app"}
The `App` component sets up global config and is required for **Toast**, **Tooltip** and **programmatic overlays**.
:::
#### Add the `isolate` class to your root container
:::code-group{sync="vite"}
```html [index.html (Vite)] {9}
Nuxt UI
```
```blade [resources/views/app.blade.php (Laravel Inertia)] {10}
@inertiaHead
@vite('resources/js/app.ts')
@inertia
```
```edge [resources/views/inertia_layout.edge (AdonisJS Inertia)] {10}
@inertiaHead()
@vite(['inertia/app/app.ts', `inertia/pages/${page.component}.vue`])
@inertia({ class: 'isolate' })
```
:::
:::note
This ensures styles are scoped to your app and prevents issues with overlays and stacking contexts.
:::
::
### Use a Vue template
To quickly get started with Nuxt UI, you can use the [starter template](https://github.com/nuxt-ui-templates/starter-vue){rel="nofollow"} by running:
```bash [Terminal]
npm create nuxt@latest -- --no-modules -t ui-vue
```
You can also get started with one of our [official templates](https://ui.nuxt.com/templates):
::card-group
:::card
---
color: neutral
icon: i-simple-icons-github
target: _blank
title: Starter
to: https://github.com/nuxt-ui-templates/starter-vue
---
A minimal template to get started with Nuxt UI.
:::
:::card
---
color: neutral
icon: i-simple-icons-github
target: _blank
title: Dashboard
to: https://github.com/nuxt-ui-templates/dashboard-vue
variant: subtle
---
A dashboard template with multi-column layout for building sophisticated admin interfaces.
:::
:::card
---
color: neutral
icon: i-simple-icons-github
target: _blank
title: Starter Adonis
to: https://github.com/nuxt-ui-templates/starter-adonis
---
A minimal Nuxt UI template for AdonisJS using Inertia.js.
:::
:::card
---
color: neutral
icon: i-simple-icons-github
target: _blank
title: Starter Laravel
to: https://github.com/nuxt-ui-templates/starter-laravel
---
A minimal Nuxt UI template for Laravel using Inertia.js.
:::
::
You can use the `Use this template` button on GitHub to create a new repository or use the CLI:
::code-group
```bash [Starter]
npm create nuxt@latest -- --no-modules -t ui-vue
```
```bash [Dashboard]
npm create nuxt@latest -- --no-modules -t ui-vue/dashboard
```
::
## Options
You can customize Nuxt UI by providing options in your `vite.config.ts`.
### `prefix`
Use the `prefix` option to change the prefix of the components.
- Default: `U`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
prefix: 'Nuxt'
})
]
})
```
### `ui`
Use the `ui` option to provide configuration for Nuxt UI.
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
colors: {
primary: 'green',
neutral: 'slate'
}
}
})
]
})
```
### `colorMode`
Use the `colorMode` option to enable or disable the color mode integration from `@vueuse/core`.
- Default: `true`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
colorMode: false
})
]
})
```
### `theme.colors`
Use the `theme.colors` option to define the dynamic color aliases used to generate components theme.
- Default: `['primary', 'secondary', 'success', 'info', 'warning', 'error']`{.inline,language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
theme: {
colors: ['primary', 'error']
}
})
]
})
```
::tip{to="https://ui.nuxt.com/docs/getting-started/theme/design-system#colors"}
Learn more about color customization and theming in the Theme section.
::
### `theme.transitions`
Use the `theme.transitions` option to enable or disable transitions on components.
- Default: `true`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
theme: {
transitions: false
}
})
]
})
```
::note
This option adds the `transition-colors` class on components with hover or active states.
::
### `theme.defaultVariants`
Use the `theme.defaultVariants` option to override the default `color` and `size` variants for components.
- Default: `{ color: 'primary', size: 'md' }`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
theme: {
defaultVariants: {
color: 'neutral',
size: 'sm'
}
}
})
]
})
```
### `inertia`
Use the `inertia` option to enable compatibility with [Inertia.js](https://inertiajs.com/){rel="nofollow"}.
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
inertia: true
})
]
})
```
::note
When using this option, `vue-router` is not required as Inertia.js provides its own routing system. The components that would normally use `RouterLink` will automatically use Inertia's `InertiaLink` component instead.
::
## Continuous releases
Nuxt UI uses [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new){rel="nofollow"} for continuous preview releases, providing developers with instant access to the latest features and bug fixes without waiting for official releases.
Automatic preview releases are created for all commits and PRs to the `v4` branch. Use them by replacing your package version with the specific commit hash or PR number.
```diff [package.json]
{
"dependencies": {
- "@nuxt/ui": "^4.0.0",
+ "@nuxt/ui": "https://pkg.pr.new/@nuxt/ui@4c96909",
}
}
```
::note
**pkg.pr.new** will automatically comment on PRs with the installation URL, making it easy to test changes.
::
# Migration to v4
Nuxt UI v4 marks a major milestone: **Nuxt UI and Nuxt UI Pro are now unified into a single, fully open-source and free library**. You now have access to 100+ production-ready components, all available in the `@nuxt/ui` package.
::note
Nuxt UI v4 requires **Nuxt 4** due to some dependencies. Make sure to upgrade to Nuxt 4 before migrating to Nuxt UI v4.
::
This guide provides step-by-step instructions to migrate your application from v3 to v4.
## Migrate your project
### From Nuxt UI Pro
1. Replace `@nuxt/ui-pro` with `@nuxt/ui` in your `package.json`:
::code-group{sync="pm"}
```bash [pnpm]
pnpm remove @nuxt/ui-pro
pnpm add @nuxt/ui
```
```bash [yarn]
yarn remove @nuxt/ui-pro
yarn add @nuxt/ui
```
```bash [npm]
npm uninstall @nuxt/ui-pro
npm install @nuxt/ui
```
```bash [bun]
bun remove @nuxt/ui-pro
bun add @nuxt/ui
```
::
::framework-only
#nuxt
:::div
2. Replace `@nuxt/ui-pro` with `@nuxt/ui` in your `nuxt.config.ts`:
```diff [nuxt.config.ts]
export default defineNuxtConfig({
modules: [
- '@nuxt/ui-pro',
+ '@nuxt/ui'
]
})
```
:::
#vue
:::div
2. Replace `@nuxt/ui-pro` with `@nuxt/ui` in your `vite.config.ts`:
```diff [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
- import uiPro from '@nuxt/ui-pro/vite'
+ import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
- uiPro({
+ ui({
ui: {
colors: {
primary: 'green',
neutral: 'slate'
}
}
})
]
})
```
:::
::
::framework-only
#nuxt
:::div
3. Use the `ui` key instead of `uiPro` in your `app.config.ts`:
```diff [app.config.ts]
export default defineAppConfig({
ui: {
colors: {
primary: 'green',
neutral: 'slate'
},
+ pageCard: {
+ slots: {
+ root: 'rounded-xl',
+ }
+ }
},
- uiPro: {
- pageCard: {
- slots: {
- root: 'rounded-xl',
- }
- }
- }
})
```
:::
#vue
:::div
3. Use the `ui` key instead of `uiPro` in your `vite.config.ts`:
```diff [vite.config.ts]
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
colors: {
primary: 'green',
neutral: 'slate'
},
+ pageCard: {
+ slots: {
+ root: 'rounded-xl',
+ }
+ }
},
- uiPro: {
- pageCard: {
- slots: {
- root: 'rounded-xl',
- }
- }
- }
})
]
})
```
:::
::
4. Replace `@nuxt/ui-pro` with `@nuxt/ui` in your CSS:
::framework-only
#nuxt
:::div
```diff [app/assets/css/main.css]
@import "tailwindcss";
- @import "@nuxt/ui-pro";
+ @import "@nuxt/ui";
```
::::warning
If you are upgrading to Nuxt 4 at the same time as Nuxt UI v4, make sure to update the `@source` directive to match the new directory structure.
```diff [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
- @source "../../content/**/*";
+ @source "../../../content/**/*";
```
::::
:::
#vue
:::div
```diff [src/assets/css/main.css]
@import "tailwindcss";
- @import "@nuxt/ui-pro";
+ @import "@nuxt/ui";
```
:::
::
5. Replace `@nuxt/ui-pro` with `@nuxt/ui` in your imports:
```diff
- import type { BannerProps } from '@nuxt/ui-pro'
+ import type { BannerProps } from '@nuxt/ui'
```
### From Nuxt UI
1. When upgrading from Nuxt UI v3, you simply need to update to v4:
::code-group{sync="pm"}
```bash [pnpm]
pnpm add @nuxt/ui
```
```bash [yarn]
yarn add @nuxt/ui
```
```bash [npm]
npm install @nuxt/ui
```
```bash [bun]
bun add @nuxt/ui
```
::
## Changes from v3
After upgrading to Nuxt UI v4, please note the following important changes:
### Renamed ButtonGroup
The `ButtonGroup` component has been renamed to [`FieldGroup`](https://ui.nuxt.com/docs/components/field-group):
```diff
-
+
+
-
```
### Renamed PageMarquee
The `PageMarquee` component has been renamed to [`Marquee`](https://ui.nuxt.com/docs/components/marquee):
```diff
-
+
```
### Removed PageAccordion
The `PageAccordion` component has been removed in favor of [`Accordion`](https://ui.nuxt.com/docs/components/accordion):
```diff
-
```
::note
The `PageAccordion` component was a wrapper that set `unmount-on-hide` to `false` and customized the `ui` prop.
::
### Renamed model modifiers
The `modelModifiers` shape used by [`Input`](https://ui.nuxt.com/docs/components/input), [`InputNumber`](https://ui.nuxt.com/docs/components/input-number) and [`Textarea`](https://ui.nuxt.com/docs/components/textarea) has changed in v4:
1. The `nullify` modifier was renamed to `nullable` (it converts empty/blank values to `null`).
2. A new `optional` modifier was added (it converts empty/blank values to `undefined`).
```diff
-
+
```
```diff
-
+
```
Use `nullable` when you want empty values as `null`, and `optional` when you prefer `undefined` for absent values.
### Changes to Form component
The `Form` component has been improved in v4 with better state management and nested form handling. Here are the key changes you need to be aware of:
1. Schema **transformations will only** be applied to the **`@submit` data** and will no longer mutate the form's state. This provides better predictability and prevents unexpected state mutations.
2. **Nested forms must be enabled explicitly** using the `nested` prop. This makes the component behavior more explicit and prevents accidental nested form creation.
3. **Nested forms should now provide a `name`** prop (similar to `UFormField`) and will automatically inherit their state from their parent form.
```diff
```
### Removed deprecated utilities
Some **Nuxt Content utilities** that were previously available in Nuxt UI Pro have been **removed** in v4:
- `findPageBreadcrumb`
- `findPageHeadline`
These are now fully provided by Nuxt Content. Make sure to update your imports and usage accordingly.
```diff
- import { findPageHeadline } from '@nuxt/ui-pro/utils/content'
+ import { findPageHeadline } from '@nuxt/content/utils'
- import { findPageBreadcrumb } from '@nuxt/ui-pro/utils/content'
+ import { findPageBreadcrumb } from '@nuxt/content/utils'
```
### AI SDK v5 migration (optional)
This section only applies if you're using the AI SDK and chat components (`ChatMessage`, `ChatMessages`, `ChatPrompt`, `ChatPromptSubmit`, `ChatPalette`). If you're not using AI features, you can skip this section.
1. Update `@ai-sdk/vue` and `ai` dependencies in your `package.json`:
```diff
{
"dependencies": {
- "@ai-sdk/vue": "^1.2.x",
+ "@ai-sdk/vue": "^2.0.x",
- "ai": "^4.3.x"
+ "ai": "^5.0.x"
}
}
```
2. `useChat` composable has been replaced with the new `Chat` class:
```diff
```
3. Messages now use `parts` instead of `content`:
```diff
// When manually creating messages
- setMessages([{
+ messages.push({
id: '1',
role: 'user',
- content: 'Hello world'
+ parts: [{ type: 'text', text: 'Hello world' }]
- }])
+ })
// In templates
-
+
```
4. Some methods have been renamed:
```diff
// Regenerate the last message
- reload()
+ chat.regenerate()
// Access chat state
- :messages="messages"
- :status="status"
+ :messages="chat.messages"
+ :status="chat.status"
```
5. New `getTextFromMessage` utility to extract text from AI SDK v5 message parts:
```vue
```
::note
---
target: _blank
to: https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0
---
For more details on AI SDK v5 changes, review the **official AI SDK v5 migration guide**.
::
::tip{target="_blank" to="https://github.com/nuxt/ui/pull/4698"}
View all changes from AI SDK v4 to v5 **in the upgrade PR** for a detailed migration reference.
::
# Migration to v3
Nuxt UI v3 is a new major version rebuilt from the ground up, introducing a modern architecture with significant performance improvements and an enhanced developer experience. This major release includes several breaking changes alongside powerful new features and capabilities:
- **Tailwind CSS v4**: Migration from JavaScript to CSS-based configuration
- **Reka UI**: Replacing Headless UI as the underlying component library
- **Tailwind Variants**: New styling API for component variants
This guide provides step by step instructions to migrate your application from v2 to v3.
## Migrate your project
::steps
### Update Tailwind CSS
Tailwind CSS v4 introduces significant changes to its configuration approach. The official Tailwind upgrade tool will help automate most of the migration process.
:::note
---
target: _blank
to: https://tailwindcss.com/docs/upgrade-guide#changes-from-v3
---
For a detailed walkthrough of all changes, refer to the official **Tailwind CSS v4 upgrade guide**.
:::
1. Create a `main.css` file and import it in your `nuxt.config.ts` file:
:::code-group
```css [app/assets/css/main.css]
@import "tailwindcss";
```
```ts [nuxt.config.ts]
export default defineNuxtConfig({
css: ['~/assets/css/main.css']
})
```
:::
2. Run the Tailwind CSS upgrade tool:
```bash
npx @tailwindcss/upgrade
```
### Update Nuxt UI
3. Install the latest version of the package:
:::code-group{sync="pm"}
```bash [pnpm]
pnpm add @nuxt/ui
```
```bash [yarn]
yarn add @nuxt/ui
```
```bash [npm]
npm install @nuxt/ui
```
```bash [bun]
bun add @nuxt/ui
```
:::
4. Import it in your CSS:
```css [app/assets/css/main.css] {2}
@import "tailwindcss";
@import "@nuxt/ui";
```
5. Wrap your app with the [App](https://ui.nuxt.com/docs/components/app) component:
```vue [app.vue] {2,4}
```
::
## Changes from v2
Now that you have updated your project, you can start migrating your code. Here's a comprehensive list of all the breaking changes in Nuxt UI v3.
### Updated design system
In Nuxt UI v2, we had a mix between a design system with `primary`, `gray`, `error` aliases and all the colors from Tailwind CSS. We've replaced it with a proper [design system](https://ui.nuxt.com/docs/getting-started/theme/design-system) with 7 color aliases:
| Color | Default | Description |
| ------------------------------ | -------- | ----------------------------------------------------------- |
| `primary`{color="primary"} | `green` | Main brand color, used as the default color for components. |
| `secondary`{color="secondary"} | `blue` | Secondary color to complement the primary color. |
| `success`{color="success"} | `green` | Used for success states. |
| `info`{color="info"} | `blue` | Used for informational states. |
| `warning`{color="warning"} | `yellow` | Used for warning states. |
| `error`{color="error"} | `red` | Used for form error validation states. |
| `neutral` | `slate` | Neutral color for backgrounds, text, etc. |
This change introduces several breaking changes that you need to be aware of:
- The `gray` color has been renamed to `neutral`
```diff
-
+
```
::note
You can also use the new [design tokens](https://ui.nuxt.com/docs/getting-started/theme/css-variables) to handle light and dark mode:
```diff
-
+
-
+
```
::
- The `gray`, `black` and `white` in the `color` props have been removed in favor of `neutral`:
```diff
-
+
-
+
-
+
```
- You can no longer use Tailwind CSS colors in the `color` props, use the new aliases instead:
```diff
-
+
```
::note{to="https://ui.nuxt.com/docs/getting-started/theme/design-system#colors"}
Learn how to extend the design system to add new color aliases.
::
- The color configuration in `app.config.ts` has been moved into a `colors` object:
```diff
export default defineAppConfig({
ui: {
- primary: 'green',
- gray: 'cool'
+ colors: {
+ primary: 'green',
+ neutral: 'slate'
+ }
}
})
```
### Updated theming system
Nuxt UI components are now styled using the [Tailwind Variants API](https://ui.nuxt.com/docs/getting-started/theme/components), which makes all the overrides you made using the `app.config.ts` and the `ui` prop obsolete.
- Update your [`app.config.ts`](https://ui.nuxt.com/docs/getting-started/theme/components#global-config) to override components with their new theme:
```diff
export default defineAppConfig({
ui: {
button: {
- font: 'font-bold',
- default: {
- size: 'md',
- color: 'primary'
- }
+ slots: {
+ base: 'font-medium'
+ },
+ defaultVariants: {
+ size: 'md',
+ color: 'primary'
+ }
}
}
})
```
- Update your [`ui` props](https://ui.nuxt.com/docs/getting-started/theme/components#ui-prop) to override each component's slots using their new theme:
```diff
-
+
```
::tip{to="https://ui.nuxt.com/docs/components/button#theme"}
We can't detail all the changes here but you can check each component's theme in the **Theme** section.
::
### Renamed components
We've renamed some Nuxt UI components to align with the Reka UI naming convention:
| v2 | v3 |
| ---------------------- | ------------------------------------------------------------------------------------------------------- |
| `Divider` | [`Separator`](https://ui.nuxt.com/docs/components/separator) |
| `Dropdown` | [`DropdownMenu`](https://ui.nuxt.com/docs/components/dropdown-menu) |
| `FormGroup` | [`FormField`](https://ui.nuxt.com/docs/components/form-field) |
| `Range` | [`Slider`](https://ui.nuxt.com/docs/components/slider) |
| `Toggle` | [`Switch`](https://ui.nuxt.com/docs/components/switch) |
| `Notification` | [`Toast`](https://ui.nuxt.com/docs/components/toast) |
| `VerticalNavigation` | [`NavigationMenu`](https://ui.nuxt.com/docs/components/navigation-menu) with `orientation="vertical"` |
| `HorizontalNavigation` | [`NavigationMenu`](https://ui.nuxt.com/docs/components/navigation-menu) with `orientation="horizontal"` |
Here are the Nuxt UI Pro components that have been renamed or removed:
| v1 | v3 |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `BlogList` | [`BlogPosts`](https://ui.nuxt.com/docs/components/blog-posts) |
| `ColorModeToggle` | [`ColorModeSwitch`](https://ui.nuxt.com/docs/components/color-mode-switch) |
| `DashboardCard` | Removed (use [`PageCard`](https://ui.nuxt.com/docs/components/page-card) instead) |
| `DashboardLayout` | [`DashboardGroup`](https://ui.nuxt.com/docs/components/dashboard-group) |
| `DashboardModal` | Removed (use [`Modal`](https://ui.nuxt.com/docs/components/modal) instead) |
| `DashboardNavbarToggle` | [`DashboardSidebarToggle`](https://ui.nuxt.com/docs/components/dashboard-sidebar-toggle) |
| `DashboardPage` | Removed |
| `DashboardPanelContent` | Removed (use `#body` slot instead) |
| `DashboardPanelHandle` | [`DashboardResizeHandle`](https://ui.nuxt.com/docs/components/dashboard-resize-handle) |
| `DashboardSection` | Removed (use [`PageCard`](https://ui.nuxt.com/docs/components/page-card) instead) |
| `DashboardSidebarLinks` | Removed (use [`NavigationMenu`](https://ui.nuxt.com/docs/components/navigation-menu) instead) |
| `DashboardSlideover` | Removed (use [`Slideover`](https://ui.nuxt.com/docs/components/slideover) instead) |
| `FooterLinks` | Removed (use [`NavigationMenu`](https://ui.nuxt.com/docs/components/navigation-menu) instead) |
| `HeaderLinks` | Removed (use [`NavigationMenu`](https://ui.nuxt.com/docs/components/navigation-menu) instead) |
| `LandingCard` | Removed (use [`PageCard`](https://ui.nuxt.com/docs/components/page-card) instead) |
| `LandingCTA` | [`PageCTA`](https://ui.nuxt.com/docs/components/page-cta) |
| `LandingFAQ` | Removed (use [`Accordion`](https://ui.nuxt.com/docs/components/accordion) instead) |
| `LandingGrid` | Removed (use [`PageGrid`](https://ui.nuxt.com/docs/components/page-grid) instead) |
| `LandingHero` | Removed (use [`PageHero`](https://ui.nuxt.com/docs/components/page-hero) instead) |
| `LandingLogos` | [`PageLogos`](https://ui.nuxt.com/docs/components/page-logos) |
| `LandingSection` | [`PageSection`](https://ui.nuxt.com/docs/components/page-section) |
| `LandingTestimonial` | Removed (use [`PageCard`](https://ui.nuxt.com/docs/components/page-card#as-a-testimonial) instead) |
| `NavigationAccordion` | [`ContentNavigation`](https://ui.nuxt.com/docs/components/content-navigation) |
| `NavigationLinks` | [`ContentNavigation`](https://ui.nuxt.com/docs/components/content-navigation) |
| `NavigationTree` | [`ContentNavigation`](https://ui.nuxt.com/docs/components/content-navigation) |
| `PageError` | [`Error`](https://ui.nuxt.com/docs/components/error) |
| `PricingCard` | [`PricingPlan`](https://ui.nuxt.com/docs/components/pricing-plan) |
| `PricingGrid` | [`PricingPlans`](https://ui.nuxt.com/docs/components/pricing-plans) |
| `PricingSwitch` | Removed (use [`Switch`](https://ui.nuxt.com/docs/components/switch) or [`Tabs`](https://ui.nuxt.com/docs/components/tabs) instead) |
### Changed components
In addition to the renamed components, there are lots of changes to the components API. Let's detail the most important ones:
- The `links` and `options` props have been renamed to `items` for consistency:
```diff
-
+
-
+
```
::note
This change affects the following components: `Breadcrumb`, `HorizontalNavigation`, `InputMenu`, `RadioGroup`, `Select`, `SelectMenu`, `VerticalNavigation`.
::
- The `click` field in different components has been removed in favor of the native Vue `onClick` event:
```diff
```
::note
This change affects the `Toast` component as well as all component that have `items` links like `NavigationMenu`, `DropdownMenu`, `CommandPalette`, etc.
::
- The global `Modals`, `Slideovers` and `Notifications` components have been removed in favor the [App](https://ui.nuxt.com/docs/components/app) component:
```diff [app.vue]
+
+
+
-
-
-
```
- The `v-model:open` directive and `default-open` prop are now used to control visibility:
```diff
-
+
```
::note
This change affects the following components: `ContextMenu`, `Modal` and `Slideover` and enables controlling visibility for `InputMenu`, `Select`, `SelectMenu` and `Tooltip`.
::
- The default slot is now used for the trigger and the content goes inside the `#content` slot (you don't need to use a `v-model:open` directive with this method):
```diff
-
-
+
+
+
+
```
::note
This change affects the following components: `Modal`, `Popover`, `Slideover`, `Tooltip`.
::
- A `#header`, `#body` and `#footer` slots have been added inside the `#content` slot like the `Card` component:
```diff
-
+
-
+
+
-
```
::note
This change affects the following components: `Modal`, `Slideover`.
::
### Changed composables
- The `useToast()` composable `timeout` prop has been renamed to `duration`:
```diff
```
- The `useModal` and `useSlideover` composables have been removed in favor of a more generic `useOverlay` composable:
Some important differences:
- The `useOverlay` composable is now used to create overlay instances
- Overlays that are opened, can be awaited for their result
- Overlays can no longer be close using `modal.close()` or `slideover.close()`, rather, they close automatically: either when a `close` event is fired explicitly from the opened component OR when the overlay closes itself (clicking on backdrop, pressing the ESC key, etc)
- To capture the return value in the parent component you must explictly emit a `close` event with the desired value
```diff
```
Props are now passed through a props attribute:
```diff
```
Closing a modal is now done through the `close` event. The `modal.open` method now returns an instance that can be used to await for the result of the modal whenever the modal is closed:
```diff
```
### Changed form validation
- The error object property for targeting form fields has been renamed from `path` to `name`:
```diff
```
---
::warning
This page is a work in progress, we'll improve it regularly.
::
# Contribution
Nuxt UI thrives thanks to its incredible community ❤️. We welcome all contributions through bug reports, pull requests, and feedback to help make this library even better.
::caution
Before reporting a bug or requesting a feature, make sure that you have read through our [documentation](https://ui.nuxt.com/){rel="nofollow"} and existing [issues](https://github.com/nuxt/ui/issues?q=is%3Aissue%20is%3Aopen%20sort%3Aupdated-desc){rel="nofollow"}.
::
## Project structure
Here's an overview of the key directories and files in the Nuxt UI project structure:
### Documentation
The documentation lives in the `docs` folder as a Nuxt app using `@nuxt/content` to generate pages from Markdown files. See the [Nuxt Content documentation](https://content.nuxt.com/docs/getting-started){rel="nofollow"} for details on how it works. Here's a breakdown of its structure:
```bash
├── app/
│ ├── assets/
│ ├── components/
│ │ └── content/
│ │ └── examples # Components used in documentation as examples
│ ├── composables/
│ └── ...
├── content/
│ ├── 1.getting-started
│ ├── 2.composables
│ └── 3.components # Components documentation
```
### Module
The module code resides in the `src` folder. Here's a breakdown of its structure:
```bash
├── plugins/
├── runtime/
│ ├── components/ # Where all the components are located
│ │ ├── Accordion.vue
│ │ ├── Alert.vue
│ │ └── ...
│ ├── composables/
│ ├── locale/
│ ├── plugins/
│ ├── types/
│ ├── utils/
│ └── vue/
│ ├── components/
│ └── plugins/
├── theme/ # This where the theme for each component is located
│ ├── accordion.ts # Theme for Accordion component
│ ├── alert.ts
│ └── ...
└── module.ts
```
## CLI
To make development easier, we've created a CLI that you can use to generate components and locales. You can access it using the `nuxt-ui make` command.
First, you need to link the CLI to your global environment:
```sh
npm link
```
### Components
You can create new components using the following command:
```sh
nuxt-ui make component [options]
```
Available options:
- `--primitive` Create a primitive component
- `--prose` Create a prose component
- `--content` Create a content component
- `--template` Only generate specific template (available templates: `playground`, `docs`, `test`, `theme`, `component`)
Example:
```sh
# Create a basic component
nuxt-ui make component my-component
# Create a prose component
nuxt-ui make component heading --prose
# Create a content component
nuxt-ui make component block --content
# Generate only documentation template
nuxt-ui make component my-component --template=docs
```
::note
When creating a new component, the CLI will automatically generate all the necessary files like the component itself, theme, tests, and documentation.
::
### Locales
You can create new locales using the following command:
```sh
nuxt-ui make locale --code --name
```
::note
---
to: https://ui.nuxt.com/docs/getting-started/integrations/i18n/nuxt#supported-languages
---
Learn more about **i18n** in the documentation.
::
## Submit a Pull Request (PR)
Before you start, check if there's an existing issue describing the problem or feature request you're working on. If there is, please leave a comment on the issue to let us know you're working on it.
If there isn't, open a new issue to discuss the problem or feature.
### Local development
To begin local development, follow these steps:
::steps{level="4"}
#### Clone the `nuxt/ui` repository to your local machine
```sh
git clone -b v4 https://github.com/nuxt/ui.git
```
#### Enable [Corepack](https://github.com/nodejs/corepack){rel="nofollow"}
```sh
corepack enable
```
#### Install dependencies
```sh
pnpm install
```
#### Generate type stubs
```sh
pnpm run dev:prepare
```
#### Start development
- To work on the **documentation** located in the `docs` folder, run:
```sh
pnpm run docs
```
- To test the Nuxt components using the **playground**, run:
```sh
pnpm run dev
```
- To test the Vue components using the **playground**, run:
```sh
pnpm run dev:vue
```
::
::note{to="https://ui.nuxt.com/#cli"}
If you're working on implementing a new component, check the **CLI** section to kickstart the process.
::
### IDE Setup
We recommend using VSCode alongside the [ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint){rel="nofollow"}. You can enable auto-fix and formatting when saving your code. Here's how:
```json
{
"editor.codeActionsOnSave": {
"source.fixAll": false,
"source.fixAll.eslint": true
}
}
```
::warning
Since ESLint is already configured to format the code, there's no need for duplicating functionality with **Prettier**. If you have it installed in your editor, we recommend disabling it to avoid conflicts.
::
### Linting
You can use the `lint` command to check for linting errors:
```sh
pnpm run lint # check for linting errors
pnpm run lint:fix # fix linting errors
```
### Type checking
We use TypeScript for type checking. You can use the `typecheck` command to check for type errors:
```sh
pnpm run typecheck
```
### Testing
Before submitting a PR, ensure that you run the tests for both `nuxt` and `vue`:
```sh
pnpm run test # for Nuxt
pnpm run test:vue # for Vue
```
::tip
If you have to update the snapshots, press `u` after the tests have finished running.
::
### Commit conventions
We use [Conventional Commits](https://www.conventionalcommits.org/){rel="nofollow"} for commit messages, which allows a changelog to be auto-generated based on the commits. Please read the [guide](https://www.conventionalcommits.org/en/v1.0.0/#summary){rel="nofollow"} through if you aren't familiar with it already.
- Use `fix` and `feat` for code changes that affect functionality or logic
- Use `docs` for documentation changes and `chore` for maintenance tasks
### Making a Pull Request
- Follow along the [instructions](https://github.com/nuxt/ui/blob/v4/.github/PULL_REQUEST_TEMPLATE.md?plain=1){rel="nofollow"} provided when creating a PR
- Ensure your PR's title adheres to the [Conventional Commits](https://www.conventionalcommits.org/){rel="nofollow"} since it will be used once the code is merged.
- Multiple commits are fine; no need to rebase or force push. We'll use `Squash and Merge` when merging.
- Ensure `lint`, `typecheck` and `tests` work before submitting the PR. Avoid making unrelated changes.
We'll review it promptly. If assigned to a maintainer, they'll review it carefully. Ignore the red text; it's for tracking purposes.
## Thanks
Thank you again for being interested in this project! You are awesome! ❤️
# Design System
## Tailwind CSS
Tailwind CSS uses a CSS-first configuration, letting you define your design tokens with the [`@theme`](https://tailwindcss.com/docs/functions-and-directives#theme-directive){rel="nofollow"} directive directly in your CSS. This makes your theme portable, maintainable and easy to customize.
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
/* Your custom design tokens go here */
}
```
::note{target="_blank" to="https://tailwindcss.com/docs/theme"}
Check the Tailwind CSS documentation for all available theme variable customization options.
::
### Fonts
Use the `--font-*` theme variables to [customize the font family utilities](https://tailwindcss.com/docs/font-family#customizing-your-theme){rel="nofollow"} in your project.
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
--font-sans: 'Public Sans', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', monospace;
}
```
::framework-only
#nuxt
:::note{to="https://ui.nuxt.com/docs/getting-started/integrations/fonts"}
Fonts defined here are automatically loaded and optimized by the `@nuxt/fonts` module.
:::
::
### Colors
Use the `--color-*` theme variables to [customize your colors](https://tailwindcss.com/docs/colors#customizing-your-colors){rel="nofollow"} or [override default colors](https://tailwindcss.com/docs/colors#overriding-default-colors){rel="nofollow"}.
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
@theme static {
/* Override default green color */
--color-green-50: #EFFDF5;
--color-green-100: #D9FBE8;
--color-green-200: #B3F5D1;
--color-green-300: #75EDAE;
--color-green-400: #00DC82;
--color-green-500: #00C16A;
--color-green-600: #00A155;
--color-green-700: #007F45;
--color-green-800: #016538;
--color-green-900: #0A5331;
--color-green-950: #052E16;
/* Define new custom color */
--color-brand-50: #fef2f2;
--color-brand-100: #fee2e2;
--color-brand-200: #fecaca;
--color-brand-300: #fca5a5;
--color-brand-400: #f87171;
--color-brand-500: #ef4444;
--color-brand-600: #dc2626;
--color-brand-700: #b91c1c;
--color-brand-800: #991b1b;
--color-brand-900: #7f1d1d;
--color-brand-950: #450a0a;
}
```
::warning
When adding custom colors, make sure to define all shades from `50` to `950` for each color.
::
### Breakpoints
Use the `--breakpoint-*` theme variables to [customize your breakpoints](https://tailwindcss.com/docs/responsive-design#customizing-your-theme){rel="nofollow"}.
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
--breakpoint-3xl: 1920px;
--breakpoint-4xl: 2560px;
--breakpoint-5xl: 3840px;
}
```
## Colors
Nuxt UI's color system is designed around **semantic naming** rather than specific color values. This approach makes your UI more maintainable and allows for easy theme switching.
### Semantic colors
Nuxt UI provides semantic color aliases that describe the **purpose** of the color. Each alias is defined based on a color from your `@theme` configuration, which can be any color you define in addition to the [default Tailwind CSS palette](https://tailwindcss.com/docs/colors){rel="nofollow"}.
| Color | Default | Description |
| ------------------------------ | -------- | ----------------------------------------------------------------- |
| `primary`{color="primary"} | `green` | Main CTAs, active navigation, brand elements, important links |
| `secondary`{color="secondary"} | `blue` | Secondary buttons, alternative actions, complementary UI elements |
| `success`{color="success"} | `green` | Success messages, completed states, positive confirmations |
| `info`{color="info"} | `blue` | Info alerts, tooltips, help text, neutral notifications |
| `warning`{color="warning"} | `yellow` | Warning messages, pending states, attention-needed items |
| `error`{color="error"} | `red` | Error messages, validation errors, destructive actions |
| `neutral` | `slate` | Text, borders, backgrounds, disabled states |
These semantic colors are available in the `color` prop of Nuxt UI components:
```vue
Save Changes
```
::note
Try the :prose-icon{.text-primary name="i-lucide-swatch-book"} theme picker in the header to instantly see how different color schemes affect the entire UI!
::
### Runtime configuration
::framework-only
#nuxt
:::div
You can configure these colors at runtime in your [`app.config.ts`](https://nuxt.com/docs/guide/directory-structure/app-config#app-config-file){rel="nofollow"} file under the `ui.colors` key, allowing for dynamic theme customization without restarting the server:
```ts [app.config.ts]
export default defineAppConfig({
ui: {
colors: {
primary: 'blue',
secondary: 'purple',
neutral: 'zinc'
}
}
})
```
:::
#vue
:::div
You can configure these colors at runtime in your `vite.config.ts` file under the `ui.colors` key, allowing for dynamic theme customization:
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
colors: {
primary: 'blue',
secondary: 'purple',
neutral: 'zinc'
}
}
})
]
})
```
:::
::
::caution
You can only use colors that exist in your theme. Either:
- Use [Tailwind's default colors](https://tailwindcss.com/docs/colors){rel="nofollow"} (like `blue`, `green`, `zinc`)
- Define custom colors first using the `@theme` directive (like `brand` in our example above)
::
### Extend colors
You may want to define extra semantic colors beyond the defaults, such as adding a `tertiary` color:
::framework-only
#nuxt
:::div
First, register the new color in your `nuxt.config.ts` under the `ui.theme.colors` key:
```ts [nuxt.config.ts] {7}
export default defineNuxtConfig({
ui: {
theme: {
colors: [
'primary',
'secondary',
'tertiary',
'info',
'success',
'warning',
'error'
]
}
}
})
```
Then, assign it in your `app.config.ts` under the `ui.colors` key:
```ts [app.config.ts] {6}
export default defineAppConfig({
ui: {
colors: {
primary: 'blue',
secondary: 'purple',
tertiary: 'indigo'
}
}
})
```
:::
#vue
:::div
Register and assign the new color in your `vite.config.ts` file:
```ts [vite.config.ts] {13,24}
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
theme: {
colors: [
'primary',
'secondary',
'tertiary',
'info',
'success',
'warning',
'error'
]
},
ui: {
colors: {
primary: 'blue',
secondary: 'purple',
tertiary: 'indigo'
}
}
})
]
})
```
:::
::
Finally, use this new color in components that support the `color` prop or [as a class](https://ui.nuxt.com/docs/getting-started/theme/css-variables):
```vue
Special Action
```
# CSS Variables
## Colors
Nuxt UI provides Tailwind CSS utility classes for each [semantic color](https://ui.nuxt.com/docs/getting-started/theme/design-system#semantic-colors) you define, allowing you to use class names like `text-error` or `bg-success`:
::code-preview
[Primary]{.text-primary.text-sm.px-4.inline-block class="py-1.5"}[Secondary]{.text-secondary.text-sm.px-4.inline-block class="py-1.5"}[Success]{.text-success.text-sm.px-4.inline-block class="py-1.5"}[Info]{.text-info.text-sm.px-4.inline-block class="py-1.5"}[Warning]{.text-warning.text-sm.px-4.inline-block class="py-1.5"}[Error]{.text-error.text-sm.px-4.inline-block class="py-1.5"}
#code
```vue
PrimarySecondarySuccessInfoWarningError
```
::
Each utility class uses a CSS variable to set its color for light and dark modes:
::code-group
```css [Light]
:root {
--ui-primary: var(--ui-color-primary-500);
--ui-secondary: var(--ui-color-secondary-500);
--ui-success: var(--ui-color-success-500);
--ui-info: var(--ui-color-info-500);
--ui-warning: var(--ui-color-warning-500);
--ui-error: var(--ui-color-error-500);
}
```
```css [Dark]
.dark {
--ui-primary: var(--ui-color-primary-400);
--ui-secondary: var(--ui-color-secondary-400);
--ui-success: var(--ui-color-success-400);
--ui-info: var(--ui-color-info-400);
--ui-warning: var(--ui-color-warning-400);
--ui-error: var(--ui-color-error-400);
}
```
::
::tip
You can adjust which shade each utility class uses for light and dark mode in your `main.css` file:
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-primary: var(--ui-color-primary-700);
}
.dark {
--ui-primary: var(--ui-color-primary-200);
}
```
::
::warning
You can't use `primary: 'black'` in your [**config**](https://ui.nuxt.com/docs/getting-started/theme/design-system#runtime-configuration) because `black` doesn't have multiple shades. To use solid black or white as your primary color, set it directly in your `main.css` file:
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-primary: black;
}
.dark {
--ui-primary: white;
}
```
::
## Text
Nuxt UI provides Tailwind CSS utility classes for text colors, allowing you to use class names like `text-dimmed` or `text-muted`:
::code-preview
[Dimmed]{.text-dimmed.text-sm.px-4.inline-block.rounded-md class="py-1.5"}[Muted]{.text-muted.text-sm.px-4.inline-block.rounded-md class="py-1.5"}[Toned]{.text-toned.text-sm.px-4.inline-block.rounded-md class="py-1.5"}[Text]{.text-default.text-sm.px-4.inline-block.rounded-md class="py-1.5"}[Highlighted]{.text-highlighted.text-sm.px-4.inline-block.rounded-md class="py-1.5"}[Inverted]{.text-inverted.bg-inverted.text-sm.px-4.inline-block.rounded-md class="py-1.5"}
#code
```vue
DimmedMutedTonedTextHighlightedInverted
```
::
Each utility class uses a CSS variable to set its color for light and dark modes:
::code-group
```css [Light]
:root {
--ui-text-dimmed: var(--ui-color-neutral-400);
--ui-text-muted: var(--ui-color-neutral-500);
--ui-text-toned: var(--ui-color-neutral-600);
--ui-text: var(--ui-color-neutral-700);
--ui-text-highlighted: var(--ui-color-neutral-900);
--ui-text-inverted: var(--color-white);
}
```
```css [Dark]
.dark {
--ui-text-dimmed: var(--ui-color-neutral-500);
--ui-text-muted: var(--ui-color-neutral-400);
--ui-text-toned: var(--ui-color-neutral-300);
--ui-text: var(--ui-color-neutral-200);
--ui-text-highlighted: var(--color-white);
--ui-text-inverted: var(--ui-color-neutral-900);
}
```
::
::tip
You can customize these CSS variables in your `main.css` file:
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-text: var(--ui-color-neutral-900);
}
.dark {
--ui-text: var(--color-white);
}
```
::
## Background
Nuxt UI provides Tailwind CSS utility classes for background colors, allowing you to use class names like `bg-default` or `bg-muted`:
::code-preview
[Default]{.bg-default.text-sm.px-4.inline-block.rounded-md.mr-2 class="py-1.5"}[Muted]{.bg-muted.text-sm.px-4.inline-block.rounded-md.mr-2 class="py-1.5"}[Elevated]{.bg-elevated.text-sm.px-4.inline-block.rounded-md.mr-2 class="py-1.5"}[Accented]{.bg-accented.text-sm.px-4.inline-block.rounded-md.mr-2 class="py-1.5"}[Inverted]{.bg-inverted.text-inverted.text-sm.px-4.inline-block.rounded-md class="py-1.5"}
#code
```vue
Default
Muted
Elevated
Accented
Inverted
```
::
Each utility class uses a CSS variable to set its color for light and dark modes:
::code-group
```css [Light]
:root {
--ui-bg: var(--color-white);
--ui-bg-muted: var(--ui-color-neutral-50);
--ui-bg-elevated: var(--ui-color-neutral-100);
--ui-bg-accented: var(--ui-color-neutral-200);
--ui-bg-inverted: var(--ui-color-neutral-900);
}
```
```css [Dark]
.dark {
--ui-bg: var(--ui-color-neutral-900);
--ui-bg-muted: var(--ui-color-neutral-800);
--ui-bg-elevated: var(--ui-color-neutral-800);
--ui-bg-accented: var(--ui-color-neutral-700);
--ui-bg-inverted: var(--color-white);
}
```
::
::tip
You can customize these CSS variables in your `main.css` file:
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-bg: var(--ui-color-neutral-50);
}
.dark {
--ui-bg: var(--ui-color-neutral-950);
}
```
::
## Border
Nuxt UI provides Tailwind CSS utility classes for border colors, allowing you to use class names like `border-default` or `border-muted`:
::code-preview
[Default]{.border-2.border-default.text-sm.px-4.inline-block.rounded-md.mr-2 class="py-1.5"}[Muted]{.border-2.border-muted.text-sm.px-4.inline-block.rounded-md.mr-2 class="py-1.5"}[Accented]{.border-2.border-accented.text-sm.px-4.inline-block.rounded-md.mr-2 class="py-1.5"}[Inverted]{.border-2.border-inverted.text-sm.px-4.inline-block.rounded-md class="py-1.5"}
#code
```vue
Default
Muted
Accented
Inverted
```
::
Each utility class uses a CSS variable to set its color for light and dark modes:
::code-group
```css [Light]
:root {
--ui-border: var(--ui-color-neutral-200);
--ui-border-muted: var(--ui-color-neutral-200);
--ui-border-accented: var(--ui-color-neutral-300);
--ui-border-inverted: var(--ui-color-neutral-900);
}
```
```css [Dark]
.dark {
--ui-border: var(--ui-color-neutral-800);
--ui-border-muted: var(--ui-color-neutral-700);
--ui-border-accented: var(--ui-color-neutral-700);
--ui-border-inverted: var(--color-white);
}
```
::
::tip
You can customize these CSS variables in your `main.css` file:
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-border: var(--ui-color-neutral-100);
}
.dark {
--ui-border: var(--ui-color-neutral-900);
}
```
::
## Radius
Nuxt UI overrides Tailwind CSS's default `rounded-*` utilities with a unified border radius system, allowing you to use regular [border radius utilities](https://tailwindcss.com/docs/border-radius){rel="nofollow"} like `rounded-xs` or `rounded-2xl`:
::code-preview
[xs]{.border-2.border-accented.text-sm.px-4.inline-block.rounded-xs.mr-2 class="py-1.5"}[sm]{.border-2.border-accented.text-sm.px-4.inline-block.rounded-sm.mr-2 class="py-1.5"}[md]{.border-2.border-accented.text-sm.px-4.inline-block.rounded-md.mr-2 class="py-1.5"}[lg]{.border-2.border-accented.text-sm.px-4.inline-block.rounded-lg.mr-2 class="py-1.5"}[xl]{.border-2.border-accented.text-sm.px-4.inline-block.rounded-xl.mr-2 class="py-1.5"}[2xl]{.border-2.border-accented.text-sm.px-4.inline-block.rounded-2xl.mr-2 class="py-1.5"}[3xl]{.border-2.border-accented.text-sm.px-4.inline-block.rounded-3xl.mr-2 class="py-1.5"}
#code
```vue
xs
sm
md
lg
xl
2xl
3xl
```
::
These utility classes are calculated based on a global `--ui-radius` CSS variable, which defines the base radius value applied across all components for a consistent look.
```css
:root {
--ui-radius: 0.25rem;
}
```
::tip
You can customize the base radius value in your `main.css` file:
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-radius: 0.5rem;
}
```
::
::note
Try the :prose-icon{.text-primary name="i-lucide-swatch-book"} theme picker in the header above to change the base radius value.
::
## Container
Nuxt UI provides a `--ui-container` CSS variable that controls the maximum width of the [Container](https://ui.nuxt.com/docs/components/container) component.
```css
:root {
--ui-container: var(--container-7xl);
}
```
::tip
You can customize this value in your `main.css` file to adjust container widths consistently throughout your application:
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
--container-8xl: 90rem;
}
:root {
--ui-container: var(--container-8xl);
}
```
::
## Header
Nuxt UI provides a `--ui-header-height` CSS variable that controls the height of the [Header](https://ui.nuxt.com/docs/components/header) component.
```css
:root {
--ui-header-height: --spacing(16);
}
```
::tip
You can customize this value in your `main.css` to adjust header height consistently throughout your application:
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-header-height: --spacing(24);
}
```
::
## Body
Nuxt UI applies default classes on the ``{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="html"} element of your app for consistent theming across light and dark modes:
```css
body {
@apply antialiased text-default bg-default scheme-light dark:scheme-dark;
}
```
# Customize components
## Tailwind Variants
Nuxt UI components are styled using the [Tailwind Variants](https://www.tailwind-variants.org/){rel="nofollow"} API, which provides a powerful way to create variants and manage component styles.
### Slots
Components can have multiple `slots`, each representing a distinct HTML element or section within the component. These slots allow for flexible content insertion and styling.
Let's take the [Card](https://ui.nuxt.com/docs/components/card) component as an example which has multiple slots:
::code-group
```ts [src/theme/card.ts]
export default {
slots: {
root: 'bg-default ring ring-default divide-y divide-default rounded-lg',
header: 'p-4 sm:px-6',
body: 'p-4 sm:p-6',
footer: 'p-4 sm:px-6'
}
}
```
```vue [src/runtime/components/Card.vue]
```
::
Some components don't have slots, they are just composed of a single root element. In this case, the theme only defines the `base` slot like the [Container](https://ui.nuxt.com/docs/components/container) component for example:
::code-group
```ts [src/theme/container.ts]
export default {
base: 'max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8'
}
```
```vue [src/runtime/components/Container.vue]
```
::
::warning
Components without slots don't have a [`ui` prop](https://ui.nuxt.com/#ui-prop), only the [`class` prop](https://ui.nuxt.com/#class-prop) is available to override styles.
::
### Variants
Components support `variants`, which allow you to dynamically adjust the styles of different `slots` based on component props.
For example, the [Avatar](https://ui.nuxt.com/docs/components/avatar) component uses a `size` variant to control its appearance:
```ts [src/theme/avatar.ts] {6-18}
export default {
slots: {
root: 'inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle bg-elevated',
image: 'h-full w-full rounded-[inherit] object-cover'
},
variants: {
size: {
sm: {
root: 'size-7 text-sm'
},
md: {
root: 'size-8 text-base'
},
lg: {
root: 'size-9 text-lg'
}
}
},
defaultVariants: {
size: 'md'
}
}
```
This way, the `size` prop will apply the corresponding styles to the `root` slot:
```vue
```
### Default Variants
The `defaultVariants` property sets the default value for each variant when no prop is passed.
For example, the [Avatar](https://ui.nuxt.com/docs/components/avatar) component has its default size set to `md`:
```ts [src/theme/avatar.ts] {19-21}
export default {
slots: {
root: 'inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle bg-elevated',
image: 'h-full w-full rounded-[inherit] object-cover'
},
variants: {
size: {
sm: {
root: 'size-7 text-sm'
},
md: {
root: 'size-8 text-base'
},
lg: {
root: 'size-9 text-lg'
}
}
},
defaultVariants: {
size: 'md'
}
}
```
::framework-only
#nuxt
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/installation/nuxt#themedefaultvariants
---
You can use the `theme.defaultVariants` option in your `nuxt.config.ts` to override the default values for `size` and `color` for all components at once.
:::
#vue
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/installation/vue#themedefaultvariants
---
You can use the `theme.defaultVariants` option in your `vite.config.ts` to override the default values for `size` and `color` for all components at once.
:::
::
### Compound Variants
Some components use the `compoundVariants` property to apply classes when multiple variant conditions are met at the same time.
For example, the [Button](https://ui.nuxt.com/docs/components/button) component uses the `compoundVariants` property to apply classes for a specific `color` and `variant` combination:
```ts [src/theme/button.ts] {27-31}
import type { ModuleOptions } from '../module'
export default (options: Required) => ({
slots: {
base: ['rounded-md font-medium inline-flex items-center disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75', options.theme.transitions && 'transition-colors']
},
variants: {
color: {
...Object.fromEntries((options.theme.colors || []).map((color: string) => [color, ''])),
neutral: ''
},
variant: {
solid: '',
outline: '',
soft: '',
subtle: '',
ghost: '',
link: ''
}
},
compoundVariants: [
...(options.theme.colors || []).map((color: string) => ({
color,
variant: 'outline',
class: `ring ring-inset ring-${color}/50 text-${color} hover:bg-${color}/10 active:bg-${color}/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus:outline-none focus-visible:ring-2 focus-visible:ring-${color}`
})),
{
color: 'neutral',
variant: 'outline',
class: 'ring ring-inset ring-accented text-default bg-default hover:bg-elevated active:bg-elevated disabled:bg-default aria-disabled:bg-default focus:outline-none focus-visible:ring-2 focus-visible:ring-inverted'
}
],
defaultVariants: {
color: 'primary',
variant: 'solid'
}
})
```
## Customize theme
You have multiple ways to customize the appearance of Nuxt UI components, you can do it for all components at once or on a per-component basis.
::note
Tailwind Variants uses [`tailwind-merge`](https://github.com/dcastil/tailwind-merge){rel="nofollow"} under the hood to merge classes so you don't have to worry about conflicting classes.
::
::tip
You can explore the theme for each component in two ways:
- Check the `Theme` section in the documentation of each individual component.
- Browse the source code directly in the GitHub repository at [`src/theme`](https://github.com/nuxt/ui/tree/v4/src/theme){rel="nofollow"}.
::
### Global config
::framework-only
#nuxt
You can override the theme of components globally inside your `app.config.ts` by using the exact same structure as the theme object.
#vue
You can override the theme of components globally inside your `vite.config.ts` by using the exact same structure as the theme object.
::
You can customize the [`slots`](https://ui.nuxt.com/#slots), [`variants`](https://ui.nuxt.com/#variants), [`compoundVariants`](https://ui.nuxt.com/#compound-variants) and [`defaultVariants`](https://ui.nuxt.com/#default-variants) of a component to change the default theme of a component:
::framework-only
#nuxt
:::div
```ts [app.config.ts]
export default defineAppConfig({
ui: {
button: {
slots: {
base: 'font-bold'
},
variants: {
size: {
md: {
leadingIcon: 'size-4'
}
}
},
compoundVariants: [{
color: 'neutral',
variant: 'outline',
class: 'ring-default hover:bg-accented'
}],
defaultVariants: {
color: 'neutral',
variant: 'outline'
}
}
}
})
```
:::
#vue
:::div
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
button: {
slots: {
base: 'font-bold'
},
variants: {
size: {
md: {
leadingIcon: 'size-4'
}
}
},
compoundVariants: [{
color: 'neutral',
variant: 'outline',
class: 'ring-default hover:bg-accented'
}],
defaultVariants: {
color: 'neutral',
variant: 'outline'
}
}
}
})
]
})
```
:::
::
::note
In this example, `font-bold` overrides `font-medium` on all buttons, `size-4` overrides `size-5` class on the leading icon when `size="md"` and `ring-default hover:bg-accented` overrides `ring-accented hover:bg-elevated` when `color="neutral"` and `variant="outline"`. The buttons now defaults to `color="neutral"` and `variant="outline"`.
::
### `ui` prop
You can also override a component's **slots** using the `ui` prop. This takes priority over both global config and resolved `variants`.
```vue
Button
```
::note
In this example, the `trailingIcon` slot is overwritten with `size-3` even though the `md` size variant would apply a `size-5` class to it.
::
### `class` prop
The `class` prop allows you to override the classes of the `root` or `base` slot. This takes priority over both global config and resolved `variants`.
```vue
Button
```
::note
In this example, the `font-bold` class will override the default `font-medium` class on this button.
::
# Icons
::callout
---
class: hidden
icon: i-logos-vue
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/vue
---
Looking for the **Vue** version?
::
## Usage
Nuxt UI automatically registers the [`@nuxt/icon`](https://github.com/nuxt/icon){rel="nofollow"} module for you, so there's no additional setup required.
### Icon component
You can use the [Icon](https://ui.nuxt.com/docs/components/icon) component with a `name` prop to display an icon:
```vue
```
::note
You can use any name from the {rel="nofollow"} collection.
::
### Component props
Some components also have an `icon` prop to display an icon, like the [Button](https://ui.nuxt.com/docs/components/button) for example:
```vue
Button
```
## Collections
### Iconify dataset
It's highly recommended to install the icon data locally with:
::code-group{sync="pm"}
```bash [pnpm]
pnpm i @iconify-json/{collection_name}
```
```bash [yarn]
yarn add @iconify-json/{collection_name}
```
```bash [npm]
npm install @iconify-json/{collection_name}
```
::
For example, to use the `i-uil-github` icon, install its collection with `@iconify-json/uil`. This way the icons can be served locally or from your serverless functions, which is faster and more reliable on both SSR and client-side.
::note
---
target: _blank
to: https://github.com/nuxt/icon?tab=readme-ov-file#iconify-dataset
---
Read more about this in the `@nuxt/icon` documentation.
::
### Custom local collections
You can use local SVG files to create a custom Iconify collection.
For example, place your icons' SVG files under a folder of your choice, for example, `./app/assets/icons`:
```bash
assets/icons
├── add.svg
└── remove.svg
```
In your `nuxt.config.ts`, add an item in `icon.customCollections`:
```ts
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
icon: {
customCollections: [{
prefix: 'custom',
dir: './app/assets/icons'
}]
}
})
```
Then you can use the icons like this:
```vue
```
::note
---
target: _blank
to: https://github.com/nuxt/icon?tab=readme-ov-file#custom-local-collections
---
Read more about this in the `@nuxt/icon` documentation.
::
## Theme
You can change the default icons used by components in your `app.config.ts`:
::icons-theme
::
# Icons
::callout
---
class: hidden
icon: i-logos-nuxt-icon
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/nuxt
---
Looking for the **Nuxt** version?
::
## Usage
### Icon component
You can use the [Icon](https://ui.nuxt.com/docs/components/icon) component with a `name` prop to display an icon:
```vue
```
::note
You can use any name from the {rel="nofollow"} collection.
::
::warning
When using collections with a dash (`-`), you need to separate the icon name from the collection name with a colon (`:`) as `@iconify/vue` does not handle this case like `@nuxt/icon`. For example, instead of `i-simple-icons-github` you need to write `i-simple-icons:github` or `simple-icons:github`.
Learn more about the [Iconify naming convention](https://iconify.design/docs/icon-components/vue/#icon){rel="nofollow"}.
::
### Component props
Some components also have an `icon` prop to display an icon, like the [Button](https://ui.nuxt.com/docs/components/button) for example:
```vue
Button
```
## Theme
You can change the default icons used by Nuxt UI components in your `vite.config.ts`:
::icons-theme
::
# Fonts
## Usage
Nuxt UI automatically registers the [`@nuxt/fonts`](https://github.com/nuxt/fonts){rel="nofollow"} module for you, so there's no additional setup required.
### Declaration
To use a font in your Nuxt UI application, you can simply declare it in your CSS. It will be automatically loaded and optimized for you.
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
--font-sans: 'Public Sans', sans-serif;
}
```
### Configuration
You can disable the `@nuxt/fonts` module with the `ui.fonts` option in your `nuxt.config.ts`:
```ts [nuxt.config.ts]
export default defineNuxtConfig({
ui: {
fonts: false
}
})
```
# Color Mode
::callout
---
class: hidden
icon: i-logos-vue
to: https://ui.nuxt.com/docs/getting-started/integrations/color-mode/vue
---
Looking for the **Vue** version?
::
## Usage
Nuxt UI automatically registers the [`@nuxtjs/color-mode`](https://github.com/nuxt-modules/color-mode){rel="nofollow"} module for you, so there's no additional setup required.
### Components
You can use the built-in [ColorModeAvatar](https://ui.nuxt.com/docs/components/color-mode-avatar) or [ColorModeImage](https://ui.nuxt.com/docs/components/color-mode-image) components to display different images for light and dark mode and the [ColorModeButton](https://ui.nuxt.com/docs/components/color-mode-button), [ColorModeSwitch](https://ui.nuxt.com/docs/components/color-mode-switch) or [ColorModeSelect](https://ui.nuxt.com/docs/components/color-mode-select) components to switch between light and dark modes.
You can also use the [useColorMode](https://color-mode.nuxtjs.org/#usage){rel="nofollow"} composable to build your own custom component:
```vue [ColorModeButton.vue]
```
### Configuration
You can disable the `@nuxtjs/color-mode` module with the `ui.colorMode` option in your `nuxt.config.ts`:
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css'],
ui: {
colorMode: false
}
})
```
# Color Mode
::callout
---
class: hidden
icon: i-logos-nuxt-icon
to: https://ui.nuxt.com/docs/getting-started/integrations/color-mode/nuxt
---
Looking for the **Nuxt** version?
::
## Usage
Nuxt UI automatically registers the [useDark](https://vueuse.org/core/useDark){rel="nofollow"} composable as a Vue plugin, so there's no additional setup required.
### Components
You can use the built-in [ColorModeAvatar](https://ui.nuxt.com/docs/components/color-mode-avatar) or [ColorModeImage](https://ui.nuxt.com/docs/components/color-mode-image) components to display different images for light and dark mode and the [ColorModeButton](https://ui.nuxt.com/docs/components/color-mode-button), [ColorModeSwitch](https://ui.nuxt.com/docs/components/color-mode-switch) or [ColorModeSelect](https://ui.nuxt.com/docs/components/color-mode-select) components to switch between light and dark modes.
You can also use the [useColorMode](https://vueuse.org/core/useColorMode){rel="nofollow"} composable to build your own custom component:
```vue [ColorModeButton.vue]
```
### Configuration
You can disable this plugin with the `colorMode` option in your `vite.config.ts`:
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
colorMode: false
})
]
})
```
# Internationalization (i18n)
::callout
---
class: hidden
icon: i-logos-vue
to: https://ui.nuxt.com/docs/getting-started/integrations/i18n/vue
---
Looking for the **Vue** version?
::
## Usage
::note{to="https://ui.nuxt.com/docs/components/app"}
Nuxt UI provides an **App** component that wraps your app to provide global configurations.
::
### Locale
Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`:
```vue [app.vue]
```
### Custom locale
You can create your own locale using the `defineLocale` composable:
```vue [app.vue]
```
::tip
Look at the `code` parameter, there you need to pass the iso code of the language. Example:
- `hi` Hindi (language)
- `de-AT`: German (language) as used in Austria (region)
::
### Extend locale
You can customize an existing locale by overriding its `messages` or `code` using the `extendLocale` composable:
```vue [app.vue]
```
### Dynamic locale
To dynamically switch between languages, you can use the [Nuxt I18n](https://i18n.nuxtjs.org/){rel="nofollow"} module.
::steps{level="4"}
#### Install the Nuxt I18n package
:::code-group{sync="pm"}
```bash [pnpm]
pnpm add @nuxtjs/i18n
```
```bash [yarn]
yarn add @nuxtjs/i18n
```
```bash [npm]
npm install @nuxtjs/i18n
```
```bash [bun]
bun add @nuxtjs/i18n
```
:::
#### Add the Nuxt I18n module in your `nuxt.config.ts`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: [
'@nuxt/ui',
'@nuxtjs/i18n'
],
css: ['~/assets/css/main.css'],
i18n: {
locales: [{
code: 'de',
name: 'Deutsch'
}, {
code: 'en',
name: 'English'
}, {
code: 'fr',
name: 'Français'
}]
}
})
```
#### Set the `locale` prop using `useI18n`
```vue [app.vue]
```
::
### Dynamic direction
Each locale has a `dir` property which will be used by the `App` component to set the directionality of all components.
In a multilingual application, you might want to set the `lang` and `dir` attributes on the `` element dynamically based on the user's locale, which you can do with the [useHead](https://nuxt.com/docs/api/composables/use-head){rel="nofollow"} composable:
```vue [app.vue]
```
## Supported languages
::supported-languages
::
# Internationalization (i18n)
::callout
---
class: hidden
icon: i-logos-nuxt-icon
to: https://ui.nuxt.com/docs/getting-started/integrations/i18n/nuxt
---
Looking for the **Nuxt** version?
::
## Usage
::note{to="https://ui.nuxt.com/docs/components/app"}
Nuxt UI provides an **App** component that wraps your app to provide global configurations.
::
### Locale
Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`:
```vue [App.vue]
```
### Custom locale
You can create your own locale using the `defineLocale` composable:
```vue [App.vue]
```
::tip
Look at the `code` parameter, there you need to pass the iso code of the language. Example:
- `hi` Hindi (language)
- `de-AT`: German (language) as used in Austria (region)
::
### Extend locale
You can customize an existing locale by overriding its `messages` or `code` using the `extendLocale` composable:
```vue [App.vue]
```
### Dynamic locale
To dynamically switch between languages, you can use the [Vue I18n](https://vue-i18n.intlify.dev/){rel="nofollow"} plugin.
::steps{level="4"}
#### Install the Vue I18n package
:::code-group{sync="pm"}
```bash [pnpm]
pnpm add vue-i18n@11
```
```bash [yarn]
yarn add vue-i18n@11
```
```bash [npm]
npm install vue-i18n@11
```
```bash [bun]
bun add vue-i18n@11
```
:::
#### Use the Vue I18n plugin in your `main.ts`
```ts [main.ts] {3,14-26,29}
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { createI18n } from 'vue-i18n'
import ui from '@nuxt/ui/vue-plugin'
import App from './App.vue'
const app = createApp(App)
const router = createRouter({
routes: [],
history: createWebHistory()
})
const i18n = createI18n({
legacy: false,
locale: 'en',
availableLocales: ['en', 'de'],
messages: {
en: {
// ...
},
de: {
// ...
}
}
})
app.use(router)
app.use(i18n)
app.use(ui)
app.mount('#app')
```
#### Set the `locale` prop using `useI18n`
```vue [App.vue]
```
::
### Dynamic direction
Each locale has a `dir` property which will be used by the `App` component to set the directionality of all components.
In a multilingual application, you might want to set the `lang` and `dir` attributes on the `` element dynamically based on the user's locale, which you can do with the [useHead](https://unhead.unjs.io/usage/composables/use-head){rel="nofollow"} composable:
```vue [App.vue]
```
## Supported languages
::supported-languages
::
# Content
## Installation
To get started, you can follow the official [guide](https://content.nuxt.com/docs/getting-started/installation){rel="nofollow"} or in summary:
::code-group{sync="pm"}
```bash [pnpm]
pnpm add @nuxt/content
```
```bash [yarn]
yarn add @nuxt/content
```
```bash [npm]
npm install @nuxt/content
```
```bash [bun]
bun add @nuxt/content
```
::
Then, add the `@nuxt/content` module in your `nuxt.config.ts`:
```ts [nuxt.config.ts] {4}
export default defineNuxtConfig({
modules: [
'@nuxt/ui',
'@nuxt/content'
],
css: ['~/assets/css/main.css']
})
```
::caution
You need to register `@nuxt/content` after `@nuxt/ui` in the `modules` array, otherwise the prose components will not be available.
::
## Configuration
When using Tailwind CSS classes in your markdown content files, you need to ensure Tailwind can detect and generate the necessary utility classes. By default, Tailwind's automatic content detection might not pick up classes written in markdown files.
To fix this, use the [`@source` directive](https://tailwindcss.com/docs/functions-and-directives#source-directive){rel="nofollow"} in your CSS file to explicitly include your content directory:
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
@source "../../../content/**/*";
```
This ensures that:
- Tailwind scans all markdown files in your content directory
- Any utility classes used in your markdown (like `text-primary`) are included in the final CSS
- Dynamic classes in MDC components or custom Vue components within your content work properly
::tip
You can also use glob patterns to be more specific about which files to scan:
- `@source "../../../content/docs/**/*.md"` - Only scan markdown in the docs folder
- `@source "../../../content/**/*.{md,yml}"` - Include both markdown and YAML files
::
::note
---
target: _blank
to: https://tailwindcss.com/docs/detecting-classes-in-source-files
---
Learn more about Tailwind's automatic content detection and best practices for optimizing build performance.
::
## Components
You might be using `@nuxt/content` to build a documentation. To help you with that, we've built some components that you can use in your pages:
- a built-in full-text search command palette with [ContentSearch](https://ui.nuxt.com/docs/components/content-search), replacing the need for Algolia DocSearch
- a navigation tree with the [ContentNavigation](https://ui.nuxt.com/docs/components/content-navigation) component
- a sticky Table of Contents with the [ContentToc](https://ui.nuxt.com/docs/components/content-toc) component
- a prev / next navigation with the [ContentSurround](https://ui.nuxt.com/docs/components/content-surround) component
## Typography
Nuxt UI provides its own custom implementations of all prose components for seamless integration with `@nuxt/content`. This approach ensures consistent styling, complete control over typography, and perfect alignment with the Nuxt UI design system so your content always looks and feels cohesive out of the box.
::note{to="https://ui.nuxt.com/docs/typography"}
Discover the full **Typography** system and explore all available prose components for rich, consistent content presentation.
::
## Utils
### `mapContentNavigation`
This util will map the navigation from `queryCollectionNavigation` and transform it recursively into an array of objects that can be used by various components.
`mapContentNavigation(navigation, options?)`
- `navigation`: The navigation tree (array of ContentNavigationItem).
- `options` (optional):
- `labelAttribute`: (string) Which field to use as label (`title` by default)
- `deep`: (number or undefined) Controls how many levels of navigation are included (`undefined` by default : includes all levels)
**Example:** As shown in the breadcrumb example below, it's commonly used to transform the navigation data into the correct format.
```vue [app.vue]
```
# MCP Server
## What is MCP?
MCP (Model Context Protocol) is a standardized protocol that enables AI assistants to access external data sources and tools. Nuxt UI provides an MCP server that allows AI assistants like Claude Code, Cursor, and Windsurf to access component information, source code, and usage examples directly.
The MCP server provides structured access to our component library, making it easy for AI tools to understand and assist with Nuxt UI development.
## Available Resources
The Nuxt UI MCP server provides the following resources for discovery:
- **`resource://nuxt-ui/components`**: Browse all available components with categories
- **`resource://nuxt-ui/composables`**: Browse all available composables with categories
- **`resource://nuxt-ui/examples`**: Browse all available code examples
- **`resource://nuxt-ui/templates`**: Browse all available project templates
- **`resource://nuxt-ui/documentation-pages`**: Browse all available documentation pages
You're able to access these resources with tools like Claude Code by using `@`.
## Available Tools
The Nuxt UI MCP server provides the following tools organized by category:
### Component Tools
- **`list_components`**: Lists all available Nuxt UI components with their categories and basic information
- **`list_composables`**: Lists all available Nuxt UI composables with their categories and basic information
- **`get_component`**: Retrieves component documentation and details
- **`get_component_metadata`**: Retrieves detailed metadata for a component including props, slots, and events
- **`search_components_by_category`**: Searches components by category or text filter
### Template Tools
- **`list_templates`**: Lists all available Nuxt UI templates with optional category filtering
- **`get_template`**: Retrieves template details and setup instructions
### Documentation Tools
- **`list_documentation_pages`**: Lists all documentation pages
- **`get_documentation_page`**: Retrieves documentation page content by URL path
- **`list_getting_started_guides`**: Lists all getting started guides and installation instructions
### Example Tools
- **`list_examples`**: Lists all available UI examples and code demonstrations
- **`get_example`**: Retrieves specific UI example implementation code and details
### Migration Tools
- **`get_migration_guide`**: Retrieves version-specific migration guides and upgrade instructions
## Available Prompts
The Nuxt UI MCP server provides guided prompts for common workflows:
- **`find_component_for_usecase`**: Find the best component for your specific use case
- **`implement_component_with_props`**: Generate complete component implementation with proper props
- **`setup_project_with_template`**: Get guided setup instructions for project templates
You're able to access these resources with tools like Claude Code by using `/`.
## Configuration
The Nuxt UI MCP server uses HTTP transport and can be configured in different AI assistants.
### ChatGPT
::note{icon="i-lucide-info"}
**Custom connectors using MCP are available on ChatGPT for Pro and Plus accounts** on the web.
::
Follow these steps to set up Nuxt UI as a connector within ChatGPT:
1. **Enable Developer mode:**
- Go to Settings → Connectors → Advanced settings → Developer mode
2. **Open ChatGPT settings**
3. **In the Connectors tab, Create a new connector:**
- Give it a name: `Nuxt UI`
- MCP server URL: `https://ui.nuxt.com/mcp`
- Authentication: `None`
4. **Click Create**
The Nuxt UI connector will appear in the composer's "Developer mode" tool later during conversations.
### Claude Code
::note{icon="i-lucide-info"}
**Ensure Claude Code is installed** - Visit [Anthropic's documentation](https://docs.anthropic.com/en/docs/claude-code/quickstart){rel="nofollow"} for installation instructions.
::
Add the server using the CLI command:
```bash
claude mcp add --transport http nuxt-ui-remote https://ui.nuxt.com/mcp
```
### Cursor
#### Quick Install
Click the button below to install the Nuxt UI MCP server directly in Cursor:
::u-button
---
color: neutral
icon: i-custom-cursor
label: Install MCP Server
to: cursor://anysphere.cursor-deeplink/mcp/install?name=nuxt-ui&config=eyJ0eXBlIjoiaHR0cCIsInVybCI6Imh0dHBzOi8vdWkubnV4dC5jb20vbWNwIn0%3D
---
::
#### Manual Setup Instructions:
1. Open Cursor and go to "Settings" > "Tools & MCP"
2. Add the Nuxt UI MCP server configuration
Or manually create/update `.cursor/mcp.json` in your project root:
```json [.cursor/mcp.json]
{
"mcpServers": {
"nuxt-ui": {
"type": "http",
"url": "https://ui.nuxt.com/mcp"
}
}
}
```
### Le Chat Mistral
#### Setup Instructions:
1. Navigate to "Intelligence" > "Connectors"
2. Click on "Add Connector" button, then select "Custom MCP Connector"
3. Create your Custom MCP Connector:
- Connector Name : `NuxtUI`
- Connector Server : `https://ui.nuxt.com/mcp`
### Visual Studio Code
::note{icon="i-lucide-info"}
**Install required extensions** - Ensure you have [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot){rel="nofollow"} and [GitHub Copilot Chat](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat){rel="nofollow"} extensions installed.
::
#### Setup Instructions:
1. Open VS Code and access the Command Palette (Ctrl/Cmd + Shift + P)
2. Type "Preferences: Open Workspace Settings (JSON)" and select it
3. Navigate to your project's `.vscode` folder or create one if it doesn't exist
4. Create or edit the `mcp.json` file with the following configuration:
```json [.vscode/mcp.json]
{
"servers": {
"nuxt-ui": {
"type": "http",
"url": "https://ui.nuxt.com/mcp"
}
}
}
```
### Windsurf
#### Setup Instructions:
1. Open Windsurf and navigate to "Settings" > "Windsurf Settings" > "Cascade"
2. Click the "Manage MCPs" button, then select the "View raw config" option
3. Add the following configuration to your MCP settings:
```json [.codeium/windsurf/mcp_config.json]
{
"mcpServers": {
"nuxt-ui": {
"type": "http",
"url": "https://ui.nuxt.com/mcp"
}
}
}
```
### Zed
#### Setup Instructions:
1. Open Zed and go to "Settings" > "Open Settings"
2. Navigate to the JSON settings file
3. Add the following context server configuration to your settings:
```json [.config/zed/settings.json]
{
"context_servers": {
"nuxt-ui": {
"source": "custom",
"command": "npx",
"args": ["mcp-remote", "https://ui.nuxt.com/mcp"],
"env": {}
}
}
}
```
### Opencode
#### Setup Instructions:
1. In your project root, create `opencode.json`
2. Add the following configuration:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"nuxt-ui": {
"type": "remote",
"url": "https://ui.nuxt.com/mcp",
"enabled": true
},
}
}
```
## Usage Examples
Once configured, you can ask your AI assistant questions like:
- "List all available Nuxt UI components"
- "Get Button component documentation"
- "What props does Input accept?"
- "Find form-related components"
- "List dashboard templates"
- "Get template setup instructions"
- "Show installation guide"
- "Get v4 migration guide"
- "List all examples"
- "Get ContactForm example code"
The AI assistant will use the MCP server to fetch structured JSON data and provide guided assistance for Nuxt UI during development.
# LLMs.txt
## What is LLMs.txt?
LLMs.txt is a structured documentation format specifically designed for large language models (LLMs). Nuxt UI provides LLMs.txt files that contain comprehensive information about our component library, making it easy for AI tools to understand and assist with Nuxt UI development.
These files are optimized for AI consumption and contain structured information about components, APIs, usage patterns, and best practices.
## Available routes
We provide LLMs.txt routes to help AI tools access our documentation:
- **`/llms.txt`** - Contains a structured overview of all components and their documentation links (\~5K tokens)
- **`/llms-full.txt`** - Provides comprehensive documentation including implementation details, examples, theming, composables, and migration guidance (\~1M+ tokens)
## Choosing the Right File
::note{icon="i-lucide-info"}
**Most users should start with `/llms.txt`** - it contains all essential information and works with standard LLM context windows. Use `/llms-full.txt` only if you need comprehensive implementation examples and your AI tool supports large contexts (200K+ tokens).
::
## Important usage notes
::warning{icon="i-lucide-alert-triangle"}
**@-symbol must be typed manually** - When using tools like Cursor or Windsurf, the `@` symbol must be typed by hand in the chat interface. Copy-pasting breaks the tool's ability to recognize it as a context reference.
::
## Usage with AI Tools
### Cursor
Nuxt UI provides specialized LLMs.txt files that you can reference in Cursor for better AI assistance with component development.
#### How to use:
1. **Direct reference**: Mention the LLMs.txt URLs when asking questions
2. Add these specific URLs to your project context using `@docs`
[Read more about Cursor Web and Docs Search](https://docs.cursor.com/en/context/@-symbols/@-docs){rel="nofollow"}
### Windsurf
Windsurf can directly access the Nuxt UI LLMs.txt files to understand component usage and best practices.
#### Using LLMs.txt with Windsurf:
- Use `@docs` to reference specific LLMs.txt URLs
- Create persistent rules referencing these URLs in your workspace
[Read more about Windsurf Web and Docs Search](https://docs.windsurf.com/windsurf/cascade/web-search){rel="nofollow"}
### Other AI Tools
Any AI tool that supports LLMs.txt can use these routes to better understand Nuxt UI.
#### Examples for ChatGPT, Claude, or other LLMs:
- "Using Nuxt UI documentation from {rel="nofollow"}"
- "Follow complete Nuxt UI guidelines from {rel="nofollow"}"
# Accordion
## Usage
Use the Accordion component to display a list of collapsible items.
```vue
```
### Items
Use the `items` prop as an array of objects with the following properties:
- `label?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `icon?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `trailingIcon?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `content?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `value?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `disabled?: boolean`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- [`slot?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}](https://ui.nuxt.com/#with-custom-slot)
- `class?: any`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `ui?: { item?: ClassNameValue, header?: ClassNameValue, trigger?: ClassNameValue, leadingIcon?: ClassNameValue, label?: ClassNameValue, trailingIcon?: ClassNameValue, content?: ClassNameValue, body?: ClassNameValue }`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```vue
```
### Multiple
Set the `type` prop to `multiple` to allow multiple items to be active at the same time. Defaults to `single`.
```vue
```
### Collapsible
When `type` is `single`, you can set the `collapsible` prop to `false` to prevent the active item from collapsing.
```vue
```
### Unmount
Use the `unmount-on-hide` prop to prevent the content from being unmounted when the accordion is collapsed. Defaults to `true`.
```vue
```
::note
You can inspect the DOM to see each item's content being rendered.
::
### Disabled
Use the `disabled` property to disable the Accordion.
You can also disable a specific item by using the `disabled` property in the item object.
```vue
```
### Trailing Icon
Use the `trailing-icon` prop to customize the trailing [Icon](https://ui.nuxt.com/docs/components/icon) of each item. Defaults to `i-lucide-chevron-down`.
::tip
You can also set an icon for a specific item by using the `trailingIcon` property in the item object.
::
```vue
```
::framework-only
#nuxt
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/nuxt#theme
---
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
:::
#vue
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/vue#theme
---
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
:::
::
## Examples
### Control active item(s)
You can control the active item(s) by using the `default-value` prop or the `v-model` directive with the index of the item.
```vue [AccordionModelValueExample.vue]
```
::tip
You can also pass the `value` of one of the items if provided.
::
::caution
When `type="multiple"`, ensure to pass an array to the `default-value` prop or the `v-model` directive.
::
### With drag and drop
Use the [`useSortable`](https://vueuse.org/integrations/useSortable/){rel="nofollow"} composable from [`@vueuse/integrations`](https://vueuse.org/integrations/README.html){rel="nofollow"} to enable drag and drop functionality on the Accordion. This integration wraps [Sortable.js](https://sortablejs.github.io/Sortable/){rel="nofollow"} to provide a seamless drag and drop experience.
```vue [AccordionDragAndDropExample.vue]
```
### With body slot
Use the `#body` slot to customize the body of each item.
```vue [AccordionBodySlotExample.vue]
This is the {{ item.label }} panel.
```
::tip
The `#body` slot includes some pre-defined styles, use the [`#content` slot](https://ui.nuxt.com/#with-content-slot) if you want to start from scratch.
::
### With content slot
Use the `#content` slot to customize the content of each item.
```vue [AccordionContentSlotExample.vue]
This is the {{ item.label }} panel.
```
### With custom slot
Use the `slot` property to customize a specific item.
You will have access to the following slots:
- `#{{ item.slot }}`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `#{{ item.slot }}-body`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```vue [AccordionCustomSlotExample.vue]
{{ item.content }}
```
### With markdown content
You can use the [MDC](https://github.com/nuxt-modules/mdc?tab=readme-ov-file#mdc){rel="nofollow"} component from `@nuxtjs/mdc` to render markdown in the accordion items.
```vue [AccordionMarkdownExample.vue]
```
## API
### Props
```ts
/**
* Props for the Accordion component
*/
interface AccordionProps {
/**
* The element or component this component should render as.
*/
as?: any;
items?: AccordionItem[] | undefined;
/**
* The icon displayed on the right side of the trigger.
*/
trailingIcon?: string | object | undefined;
/**
* The key used to get the label from the item.
* @default "\"label\""
*/
labelKey?: GetItemKeys | undefined;
ui?: { root?: ClassNameValue; item?: ClassNameValue; header?: ClassNameValue; trigger?: ClassNameValue; content?: ClassNameValue; body?: ClassNameValue; leadingIcon?: ClassNameValue; trailingIcon?: ClassNameValue; label?: ClassNameValue; } | undefined;
/**
* When type is "single", allows closing content when clicking trigger for an open item.
* When type is "multiple", this prop has no effect.
* @default "true"
*/
collapsible?: boolean | undefined;
/**
* The default active value of the item(s).
*
* Use when you do not need to control the state of the item(s).
*/
defaultValue?: string | string[] | undefined;
/**
* The controlled value of the active item(s).
*
* Use this when you need to control the state of the items. Can be binded with `v-model`
*/
modelValue?: string | string[] | undefined;
/**
* Determines whether a "single" or "multiple" items can be selected at a time.
*
* This prop will overwrite the inferred type from `modelValue` and `defaultValue`.
* @default "\"single\""
*/
type?: SingleOrMultipleType | undefined;
/**
* When `true`, prevents the user from interacting with the accordion and all its items
*/
disabled?: boolean | undefined;
/**
* When `true`, the element will be unmounted on closed state.
* @default "true"
*/
unmountOnHide?: boolean | undefined;
}
```
### Slots
```ts
/**
* Slots for the Accordion component
*/
interface AccordionSlots {
leading(): any;
default(): any;
trailing(): any;
content(): any;
body(): any;
}
```
### Emits
```ts
/**
* Emitted events for the Accordion component
*/
interface AccordionEmits {
update:modelValue: (payload: [value: string | string[] | undefined]) => void;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
accordion: {
slots: {
root: 'w-full',
item: 'border-b border-default last:border-b-0',
header: 'flex',
trigger: 'group flex-1 flex items-center gap-1.5 font-medium text-sm py-3.5 focus-visible:outline-primary min-w-0',
content: 'data-[state=open]:animate-[accordion-down_200ms_ease-out] data-[state=closed]:animate-[accordion-up_200ms_ease-out] overflow-hidden focus:outline-none',
body: 'text-sm pb-3.5',
leadingIcon: 'shrink-0 size-5',
trailingIcon: 'shrink-0 size-5 ms-auto group-data-[state=open]:rotate-180 transition-transform duration-200',
label: 'text-start break-words'
},
variants: {
disabled: {
true: {
trigger: 'cursor-not-allowed opacity-75'
}
}
}
}
}
})
```
## Changelog
::component-changelog
::
# Alert
## Usage
### Title
Use the `title` prop to set the title of the Alert.
```vue
```
### Description
Use the `description` prop to set the description of the Alert.
```vue
```
### Icon
Use the `icon` prop to show an [Icon](https://ui.nuxt.com/docs/components/icon).
```vue
```
### Avatar
Use the `avatar` prop to show an [Avatar](https://ui.nuxt.com/docs/components/avatar).
```vue
```
### Color
Use the `color` prop to change the color of the Alert.
```vue
```
### Variant
Use the `variant` prop to change the variant of the Alert.
```vue
```
### Close
Use the `close` prop to display a [Button](https://ui.nuxt.com/docs/components/button) to dismiss the Alert.
::tip
An `update:open` event will be emitted when the close button is clicked.
::
```vue
```
You can pass any property from the [Button](https://ui.nuxt.com/docs/components/button) component to customize it.
```vue
```
### Close Icon
Use the `close-icon` prop to customize the close button [Icon](https://ui.nuxt.com/docs/components/icon). Defaults to `i-lucide-x`.
```vue
```
::framework-only
#nuxt
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/nuxt#theme
---
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
:::
#vue
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/vue#theme
---
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
:::
::
### Actions
Use the `actions` prop to add some [Button](https://ui.nuxt.com/docs/components/button) actions to the Alert.
```vue
```
### Orientation
Use the `orientation` prop to change the orientation of the Alert.
```vue
```
## Examples
### `class` prop
Use the `class` prop to override the base styles of the Alert.
```vue
```
### `ui` prop
Use the `ui` prop to override the slots styles of the Alert.
```vue
```
## API
### Props
```ts
/**
* Props for the Alert component
*/
interface AlertProps {
/**
* The element or component this component should render as.
*/
as?: any;
title?: string | undefined;
description?: string | undefined;
icon?: string | object | undefined;
avatar?: AvatarProps | undefined;
color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined;
variant?: "solid" | "outline" | "soft" | "subtle" | undefined;
/**
* The orientation between the content and the actions.
* @default "\"vertical\""
*/
orientation?: "vertical" | "horizontal" | undefined;
/**
* Display a list of actions:
* - under the title and description when orientation is `vertical`
* - next to the close button when orientation is `horizontal`
* `{ size: 'xs' }`{lang="ts-type"}
*/
actions?: ButtonProps[] | undefined;
/**
* Display a close button to dismiss the alert.
* `{ size: 'md', color: 'neutral', variant: 'link' }`{lang="ts-type"}
*/
close?: boolean | Partial | undefined;
/**
* The icon displayed in the close button.
*/
closeIcon?: string | object | undefined;
ui?: { root?: ClassNameValue; wrapper?: ClassNameValue; title?: ClassNameValue; description?: ClassNameValue; icon?: ClassNameValue; avatar?: ClassNameValue; avatarSize?: ClassNameValue; actions?: ClassNameValue; close?: ClassNameValue; } | undefined;
}
```
### Slots
```ts
/**
* Slots for the Alert component
*/
interface AlertSlots {
leading(): any;
title(): any;
description(): any;
actions(): any;
close(): any;
}
```
### Emits
```ts
/**
* Emitted events for the Alert component
*/
interface AlertEmits {
update:open: (payload: [value: boolean]) => void;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
alert: {
slots: {
root: 'relative overflow-hidden w-full rounded-lg p-4 flex gap-2.5',
wrapper: 'min-w-0 flex-1 flex flex-col',
title: 'text-sm font-medium',
description: 'text-sm opacity-90',
icon: 'shrink-0 size-5',
avatar: 'shrink-0',
avatarSize: '2xl',
actions: 'flex flex-wrap gap-1.5 shrink-0',
close: 'p-0'
},
variants: {
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
variant: {
solid: '',
outline: '',
soft: '',
subtle: ''
},
orientation: {
horizontal: {
root: 'items-center',
actions: 'items-center'
},
vertical: {
root: 'items-start',
actions: 'items-start mt-2.5'
}
},
title: {
true: {
description: 'mt-1'
}
}
},
compoundVariants: [
{
color: 'primary',
variant: 'solid',
class: {
root: 'bg-primary text-inverted'
}
},
{
color: 'secondary',
variant: 'solid',
class: {
root: 'bg-secondary text-inverted'
}
},
{
color: 'success',
variant: 'solid',
class: {
root: 'bg-success text-inverted'
}
},
{
color: 'info',
variant: 'solid',
class: {
root: 'bg-info text-inverted'
}
},
{
color: 'warning',
variant: 'solid',
class: {
root: 'bg-warning text-inverted'
}
},
{
color: 'error',
variant: 'solid',
class: {
root: 'bg-error text-inverted'
}
},
{
color: 'primary',
variant: 'outline',
class: {
root: 'text-primary ring ring-inset ring-primary/25'
}
},
{
color: 'secondary',
variant: 'outline',
class: {
root: 'text-secondary ring ring-inset ring-secondary/25'
}
},
{
color: 'success',
variant: 'outline',
class: {
root: 'text-success ring ring-inset ring-success/25'
}
},
{
color: 'info',
variant: 'outline',
class: {
root: 'text-info ring ring-inset ring-info/25'
}
},
{
color: 'warning',
variant: 'outline',
class: {
root: 'text-warning ring ring-inset ring-warning/25'
}
},
{
color: 'error',
variant: 'outline',
class: {
root: 'text-error ring ring-inset ring-error/25'
}
},
{
color: 'primary',
variant: 'soft',
class: {
root: 'bg-primary/10 text-primary'
}
},
{
color: 'secondary',
variant: 'soft',
class: {
root: 'bg-secondary/10 text-secondary'
}
},
{
color: 'success',
variant: 'soft',
class: {
root: 'bg-success/10 text-success'
}
},
{
color: 'info',
variant: 'soft',
class: {
root: 'bg-info/10 text-info'
}
},
{
color: 'warning',
variant: 'soft',
class: {
root: 'bg-warning/10 text-warning'
}
},
{
color: 'error',
variant: 'soft',
class: {
root: 'bg-error/10 text-error'
}
},
{
color: 'primary',
variant: 'subtle',
class: {
root: 'bg-primary/10 text-primary ring ring-inset ring-primary/25'
}
},
{
color: 'secondary',
variant: 'subtle',
class: {
root: 'bg-secondary/10 text-secondary ring ring-inset ring-secondary/25'
}
},
{
color: 'success',
variant: 'subtle',
class: {
root: 'bg-success/10 text-success ring ring-inset ring-success/25'
}
},
{
color: 'info',
variant: 'subtle',
class: {
root: 'bg-info/10 text-info ring ring-inset ring-info/25'
}
},
{
color: 'warning',
variant: 'subtle',
class: {
root: 'bg-warning/10 text-warning ring ring-inset ring-warning/25'
}
},
{
color: 'error',
variant: 'subtle',
class: {
root: 'bg-error/10 text-error ring ring-inset ring-error/25'
}
},
{
color: 'neutral',
variant: 'solid',
class: {
root: 'text-inverted bg-inverted'
}
},
{
color: 'neutral',
variant: 'outline',
class: {
root: 'text-highlighted bg-default ring ring-inset ring-default'
}
},
{
color: 'neutral',
variant: 'soft',
class: {
root: 'text-highlighted bg-elevated/50'
}
},
{
color: 'neutral',
variant: 'subtle',
class: {
root: 'text-highlighted bg-elevated/50 ring ring-inset ring-accented'
}
}
],
defaultVariants: {
color: 'primary',
variant: 'solid'
}
}
}
})
```
## Changelog
::component-changelog
::
# App
## Usage
This component implements Reka UI [ConfigProvider](https://reka-ui.com/docs/utilities/config-provider){rel="nofollow"} to provide global configuration to all components:
- Enables all primitives to inherit global reading direction.
- Enables changing the behavior of scroll body when setting body lock.
- Much more controls to prevent layout shifts.
It's also using [ToastProvider](https://reka-ui.com/docs/components/toast#provider){rel="nofollow"} and [TooltipProvider](https://reka-ui.com/docs/components/tooltip#provider){rel="nofollow"} to provide global toasts and tooltips, as well as programmatic modals and slideovers.
Wrap your entire application with the App component in your `app.vue` file:
```vue [app.vue]
```
::framework-only
#nuxt
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/i18n/nuxt#locale
---
Learn how to use the `locale` prop to change the locale of your app.
:::
#vue
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/i18n/vue#locale
---
Learn how to use the `locale` prop to change the locale of your app.
:::
::
## API
### Props
```ts
/**
* Props for the App component
*/
interface AppProps {
tooltip?: TooltipProviderProps | undefined;
toaster?: ToasterProps | null | undefined;
locale?: Locale | undefined;
/**
* @default "\"body\""
*/
portal?: string | boolean | HTMLElement | undefined;
/**
* The global scroll body behavior of your application. This will be inherited by the related primitives.
*/
scrollBody?: boolean | ScrollBodyOption | undefined;
/**
* The global `nonce` value of your application. This will be inherited by the related primitives.
*/
nonce?: string | undefined;
}
```
### Slots
```ts
/**
* Slots for the App component
*/
interface AppSlots {
default(): any;
}
```
## Changelog
::component-changelog
::
# AuthForm
## Usage
Built on top of the [Form](https://ui.nuxt.com/docs/components/form) component, the `AuthForm` component can be used in your pages or wrapped in a [PageCard](https://ui.nuxt.com/docs/components/page-card).
```vue [AuthFormExample.vue]
```
### Fields
The Form will construct itself based on the `fields` prop and the state will be handled internally.
Use the `fields` prop as an array of objects with the following properties:
- `name?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `type?: 'text' | 'password' | 'email' | 'number' | 'checkbox' | 'select' | 'otp'`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
Each field must include a `type` property, which determines the input component and any additional props applied: `checkbox` fields use [Checkbox](https://ui.nuxt.com/docs/components/checkbox#props) props, `select` fields use [SelectMenu](https://ui.nuxt.com/docs/components/select-menu#props) props, `otp` fields use [PinInput](https://ui.nuxt.com/docs/components/pin-input#props) props, and all other types use [Input](https://ui.nuxt.com/docs/components/input#props) props.
You can also pass any property from the [FormField](https://ui.nuxt.com/docs/components/form-field#props) component to each field.
```vue
```
### Title
Use the `title` prop to set the title of the Form.
```vue
```
### Description
Use the `description` prop to set the description of the Form.
```vue
```
### Icon
Use the `icon` prop to set the icon of the Form.
```vue
```
### Providers
Use the `providers` prop to add providers to the form.
You can pass any property from the [Button](https://ui.nuxt.com/docs/components/button) component such as `variant`, `color`, `to`, etc.
```vue
```
### Separator
Use the `separator` prop to customize the [Separator](https://ui.nuxt.com/docs/components/separator) between the providers and the fields. Defaults to `or`.
```vue
```
You can pass any property from the [Separator](https://ui.nuxt.com/docs/components/separator#props) component to customize it.
```vue
```
### Submit
Use the `submit` prop to change the submit button of the Form.
You can pass any property from the [Button](https://ui.nuxt.com/docs/components/button) component such as `variant`, `color`, `to`, etc.
```vue
```
## Examples
### Within a page
You can wrap the `AuthForm` component with the [PageCard](https://ui.nuxt.com/docs/components/page-card) component to display it within a `login.vue` page for example.
```vue [AuthFormPageExample.vue]
Don't have an account? Sign up.
Forgot password?
By signing in, you agree to our Terms of Service.
```
## API
### Props
```ts
/**
* Props for the AuthForm component
*/
interface AuthFormProps {
/**
* The element or component this component should render as.
*/
as?: any;
/**
* The icon displayed above the title.
*/
icon?: string | object | undefined;
title?: string | undefined;
description?: string | undefined;
fields?: (AuthFormInputField<"number"> | AuthFormCheckboxField | AuthFormSelectField | AuthFormOtpField | AuthFormInputField<"password"> | AuthFormInputField<"text"> | AuthFormInputField<"email">)[] | undefined;
/**
* Display a list of Button under the description.
* `{ color: 'neutral', variant: 'subtle', block: true }`{lang="ts-type"}
*/
providers?: ButtonProps[] | undefined;
/**
* The text displayed in the separator.
* @default "\"or\""
*/
separator?: string | SeparatorProps | undefined;
/**
* Display a submit button at the bottom of the form.
* `{ label: 'Continue', block: true }`{lang="ts-type"}
*/
submit?: ButtonProps | undefined;
schema?: FormSchema | undefined;
validate?: ((state: Partial) => FormError[] | Promise[]>) | undefined;
validateOn?: FormInputEvents[] | undefined;
validateOnInputDelay?: number | undefined;
disabled?: boolean | undefined;
loading?: boolean | undefined;
loadingAuto?: boolean | undefined;
ui?: { root?: ClassNameValue; header?: ClassNameValue; leading?: ClassNameValue; leadingIcon?: ClassNameValue; title?: ClassNameValue; description?: ClassNameValue; body?: ClassNameValue; providers?: ClassNameValue; checkbox?: ClassNameValue; select?: ClassNameValue; password?: ClassNameValue; otp?: ClassNameValue; input?: ClassNameValue; separator?: ClassNameValue; form?: ClassNameValue; footer?: ClassNameValue; } | undefined;
}
```
### Slots
```ts
/**
* Slots for the AuthForm component
*/
interface AuthFormSlots {
header(): any;
leading(): any;
title(): any;
description(): any;
providers(): any;
validation(): any;
submit(): any;
footer(): any;
}
```
### Emits
```ts
/**
* Emitted events for the AuthForm component
*/
interface AuthFormEmits {
submit: (payload: [payload: FormSubmitEvent]) => void;
}
```
### Expose
You can access the typed component instance (exposing formRef and state) using [`useTemplateRef`](https://vuejs.org/api/composition-api-helpers.html#usetemplateref){rel="nofollow"}. For example, in a separate form (e.g. a "reset" form) you can do:
```vue
```
This gives you access to the following (exposed) properties:
| Name | Type |
| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `formRef`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | `Ref`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} |
| `state`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | `Reactive`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} |
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
authForm: {
slots: {
root: 'w-full space-y-6',
header: 'flex flex-col text-center',
leading: 'mb-2',
leadingIcon: 'size-8 shrink-0 inline-block',
title: 'text-xl text-pretty font-semibold text-highlighted',
description: 'mt-1 text-base text-pretty text-muted',
body: 'gap-y-6 flex flex-col',
providers: 'space-y-3',
checkbox: '',
select: 'w-full',
password: 'w-full',
otp: 'w-full',
input: 'w-full',
separator: '',
form: 'space-y-5',
footer: 'text-sm text-center text-muted mt-2'
}
}
}
})
```
## Changelog
::component-changelog
::
# Avatar
## Usage
The Avatar uses the `` component when [`@nuxt/image`](https://github.com/nuxt/image){rel="nofollow"} is installed, falling back to `img` otherwise.
```vue
```
::note
You can pass any property from the HTML `` element such as `alt`, `loading`, etc.
::
### Src
Use the `src` prop to set the image URL.
```vue
```
### Size
Use the `size` prop to set the size of the Avatar.
```vue
```
::note
The `` element's `width` and `height` are automatically set based on the `size` prop.
::
### Icon
Use the `icon` prop to display a fallback [Icon](https://ui.nuxt.com/docs/components/icon).
```vue
```
### Text
Use the `text` prop to display a fallback text.
```vue
```
### Alt
When no icon or text is provided, the **initials** of the `alt` prop is used as fallback.
```vue
```
::note
The `alt` prop is passed to the `img` element as the `alt` attribute.
::
### Chip
Use the `chip` prop to display a chip around the Avatar.
```vue
```
## Examples
### With tooltip
You can use a [Tooltip](https://ui.nuxt.com/docs/components/tooltip) component to display a tooltip when hovering the Avatar.
```vue [AvatarTooltipExample.vue]
```
### With mask
You can use a CSS mask to display an Avatar with a custom shape instead of a simple circle.
```vue [AvatarMaskExample.vue]
```
## API
### Props
```ts
/**
* Props for the Avatar component
*/
interface AvatarProps {
/**
* The element or component this component should render as.
*/
as?: any;
src?: string | undefined;
alt?: string | undefined;
icon?: string | object | undefined;
text?: string | undefined;
size?: "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "3xs" | "2xl" | "3xl" | undefined;
chip?: boolean | ChipProps | undefined;
ui?: { root?: ClassNameValue; image?: ClassNameValue; fallback?: ClassNameValue; icon?: ClassNameValue; } | undefined;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
avatar: {
slots: {
root: 'inline-flex items-center justify-center shrink-0 select-none rounded-full align-middle bg-elevated',
image: 'h-full w-full rounded-[inherit] object-cover',
fallback: 'font-medium leading-none text-muted truncate',
icon: 'text-muted shrink-0'
},
variants: {
size: {
'3xs': {
root: 'size-4 text-[8px]'
},
'2xs': {
root: 'size-5 text-[10px]'
},
xs: {
root: 'size-6 text-xs'
},
sm: {
root: 'size-7 text-sm'
},
md: {
root: 'size-8 text-base'
},
lg: {
root: 'size-9 text-lg'
},
xl: {
root: 'size-10 text-xl'
},
'2xl': {
root: 'size-11 text-[22px]'
},
'3xl': {
root: 'size-12 text-2xl'
}
}
},
defaultVariants: {
size: 'md'
}
}
}
})
```
## Changelog
::component-changelog
::
# AvatarGroup
## Usage
Wrap multiple [Avatar](https://ui.nuxt.com/docs/components/avatar) within an AvatarGroup to stack them.
```vue
```
### Size
Use the `size` prop to change the size of all the avatars.
```vue
```
### Max
Use the `max` prop to limit the number of avatars displayed. The rest is displayed as an `+X` avatar.
```vue
```
## Examples
### With tooltip
Wrap each avatar with a [Tooltip](https://ui.nuxt.com/docs/components/tooltip) to display a tooltip on hover.
```vue [AvatarGroupTooltipExample.vue]
```
### With chip
Wrap each avatar with a [Chip](https://ui.nuxt.com/docs/components/chip) to display a chip around the avatar.
```vue [AvatarGroupChipExample.vue]
```
### With link
Wrap each avatar with a [Link](https://ui.nuxt.com/docs/components/link) to make them clickable.
```vue [AvatarGroupLinkExample.vue]
```
### With mask
Wrap an avatar with a CSS mask to display it with a custom shape.
```vue [AvatarGroupMaskExample.vue]
```
::warning
The `chip` prop does not work correctly when using a mask. Chips may be cut depending on the mask shape.
::
## API
### Props
```ts
/**
* Props for the AvatarGroup component
*/
interface AvatarGroupProps {
/**
* The element or component this component should render as.
*/
as?: any;
size?: "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "3xs" | "2xl" | "3xl" | undefined;
/**
* The maximum number of avatars to display.
*/
max?: string | number | undefined;
ui?: { root?: ClassNameValue; base?: ClassNameValue; } | undefined;
}
```
### Slots
```ts
/**
* Slots for the AvatarGroup component
*/
interface AvatarGroupSlots {
default(): any;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
avatarGroup: {
slots: {
root: 'inline-flex flex-row-reverse justify-end',
base: 'relative rounded-full ring-bg first:me-0'
},
variants: {
size: {
'3xs': {
base: 'ring -me-0.5'
},
'2xs': {
base: 'ring -me-0.5'
},
xs: {
base: 'ring -me-0.5'
},
sm: {
base: 'ring-2 -me-1.5'
},
md: {
base: 'ring-2 -me-1.5'
},
lg: {
base: 'ring-2 -me-1.5'
},
xl: {
base: 'ring-3 -me-2'
},
'2xl': {
base: 'ring-3 -me-2'
},
'3xl': {
base: 'ring-3 -me-2'
}
}
},
defaultVariants: {
size: 'md'
}
}
}
})
```
## Changelog
::component-changelog
::
# Badge
## Usage
Use the default slot to set the label of the Badge.
```vue
Badge
```
### Label
Use the `label` prop to set the label of the Badge.
```vue
```
### Color
Use the `color` prop to change the color of the Badge.
```vue
Badge
```
### Variant
Use the `variant` props to change the variant of the Badge.
```vue
Badge
```
### Size
Use the `size` prop to change the size of the Badge.
```vue
Badge
```
### Icon
Use the `icon` prop to show an [Icon](https://ui.nuxt.com/docs/components/icon) inside the Badge.
```vue
Badge
```
Use the `leading` and `trailing` props to set the icon position or the `leading-icon` and `trailing-icon` props to set a different icon for each position.
```vue
Badge
```
### Avatar
Use the `avatar` prop to show an [Avatar](https://ui.nuxt.com/docs/components/avatar) inside the Badge.
```vue
Badge
```
## Examples
### `class` prop
Use the `class` prop to override the base styles of the Badge.
```vue
Badge
```
## API
### Props
```ts
/**
* Props for the Badge component
*/
interface BadgeProps {
/**
* The element or component this component should render as.
* @default "\"span\""
*/
as?: any;
label?: string | number | undefined;
color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined;
variant?: "solid" | "outline" | "soft" | "subtle" | undefined;
size?: "xs" | "sm" | "md" | "lg" | "xl" | undefined;
/**
* Render the badge with equal padding on all sides.
*/
square?: boolean | undefined;
ui?: { base?: ClassNameValue; label?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; trailingIcon?: ClassNameValue; } | undefined;
/**
* Display an icon based on the `leading` and `trailing` props.
*/
icon?: string | object | undefined;
/**
* Display an avatar on the left side.
*/
avatar?: AvatarProps | undefined;
/**
* When `true`, the icon will be displayed on the left side.
*/
leading?: boolean | undefined;
/**
* Display an icon on the left side.
*/
leadingIcon?: string | object | undefined;
/**
* When `true`, the icon will be displayed on the right side.
*/
trailing?: boolean | undefined;
/**
* Display an icon on the right side.
*/
trailingIcon?: string | object | undefined;
}
```
### Slots
```ts
/**
* Slots for the Badge component
*/
interface BadgeSlots {
leading(): any;
default(): any;
trailing(): any;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
badge: {
slots: {
base: 'font-medium inline-flex items-center',
label: 'truncate',
leadingIcon: 'shrink-0',
leadingAvatar: 'shrink-0',
leadingAvatarSize: '',
trailingIcon: 'shrink-0'
},
variants: {
fieldGroup: {
horizontal: 'not-only:first:rounded-e-none not-only:last:rounded-s-none not-last:not-first:rounded-none focus-visible:z-[1]',
vertical: 'not-only:first:rounded-b-none not-only:last:rounded-t-none not-last:not-first:rounded-none focus-visible:z-[1]'
},
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
variant: {
solid: '',
outline: '',
soft: '',
subtle: ''
},
size: {
xs: {
base: 'text-[8px]/3 px-1 py-0.5 gap-1 rounded-sm',
leadingIcon: 'size-3',
leadingAvatarSize: '3xs',
trailingIcon: 'size-3'
},
sm: {
base: 'text-[10px]/3 px-1.5 py-1 gap-1 rounded-sm',
leadingIcon: 'size-3',
leadingAvatarSize: '3xs',
trailingIcon: 'size-3'
},
md: {
base: 'text-xs px-2 py-1 gap-1 rounded-md',
leadingIcon: 'size-4',
leadingAvatarSize: '3xs',
trailingIcon: 'size-4'
},
lg: {
base: 'text-sm px-2 py-1 gap-1.5 rounded-md',
leadingIcon: 'size-5',
leadingAvatarSize: '2xs',
trailingIcon: 'size-5'
},
xl: {
base: 'text-base px-2.5 py-1 gap-1.5 rounded-md',
leadingIcon: 'size-6',
leadingAvatarSize: '2xs',
trailingIcon: 'size-6'
}
},
square: {
true: ''
}
},
compoundVariants: [
{
color: 'primary',
variant: 'solid',
class: 'bg-primary text-inverted'
},
{
color: 'secondary',
variant: 'solid',
class: 'bg-secondary text-inverted'
},
{
color: 'success',
variant: 'solid',
class: 'bg-success text-inverted'
},
{
color: 'info',
variant: 'solid',
class: 'bg-info text-inverted'
},
{
color: 'warning',
variant: 'solid',
class: 'bg-warning text-inverted'
},
{
color: 'error',
variant: 'solid',
class: 'bg-error text-inverted'
},
{
color: 'primary',
variant: 'outline',
class: 'text-primary ring ring-inset ring-primary/50'
},
{
color: 'secondary',
variant: 'outline',
class: 'text-secondary ring ring-inset ring-secondary/50'
},
{
color: 'success',
variant: 'outline',
class: 'text-success ring ring-inset ring-success/50'
},
{
color: 'info',
variant: 'outline',
class: 'text-info ring ring-inset ring-info/50'
},
{
color: 'warning',
variant: 'outline',
class: 'text-warning ring ring-inset ring-warning/50'
},
{
color: 'error',
variant: 'outline',
class: 'text-error ring ring-inset ring-error/50'
},
{
color: 'primary',
variant: 'soft',
class: 'bg-primary/10 text-primary'
},
{
color: 'secondary',
variant: 'soft',
class: 'bg-secondary/10 text-secondary'
},
{
color: 'success',
variant: 'soft',
class: 'bg-success/10 text-success'
},
{
color: 'info',
variant: 'soft',
class: 'bg-info/10 text-info'
},
{
color: 'warning',
variant: 'soft',
class: 'bg-warning/10 text-warning'
},
{
color: 'error',
variant: 'soft',
class: 'bg-error/10 text-error'
},
{
color: 'primary',
variant: 'subtle',
class: 'bg-primary/10 text-primary ring ring-inset ring-primary/25'
},
{
color: 'secondary',
variant: 'subtle',
class: 'bg-secondary/10 text-secondary ring ring-inset ring-secondary/25'
},
{
color: 'success',
variant: 'subtle',
class: 'bg-success/10 text-success ring ring-inset ring-success/25'
},
{
color: 'info',
variant: 'subtle',
class: 'bg-info/10 text-info ring ring-inset ring-info/25'
},
{
color: 'warning',
variant: 'subtle',
class: 'bg-warning/10 text-warning ring ring-inset ring-warning/25'
},
{
color: 'error',
variant: 'subtle',
class: 'bg-error/10 text-error ring ring-inset ring-error/25'
},
{
color: 'neutral',
variant: 'solid',
class: 'text-inverted bg-inverted'
},
{
color: 'neutral',
variant: 'outline',
class: 'ring ring-inset ring-accented text-default bg-default'
},
{
color: 'neutral',
variant: 'soft',
class: 'text-default bg-elevated'
},
{
color: 'neutral',
variant: 'subtle',
class: 'ring ring-inset ring-accented text-default bg-elevated'
},
{
size: 'xs',
square: true,
class: 'p-0.5'
},
{
size: 'sm',
square: true,
class: 'p-1'
},
{
size: 'md',
square: true,
class: 'p-1'
},
{
size: 'lg',
square: true,
class: 'p-1'
},
{
size: 'xl',
square: true,
class: 'p-1'
}
],
defaultVariants: {
color: 'primary',
variant: 'solid',
size: 'md'
}
}
}
})
```
## Changelog
::component-changelog
::
# Banner
## Usage
### Title
Use the `title` prop to display a title on the Banner.
```vue
```
### Icon
Use the `icon` prop to display an icon on the Banner.
```vue
```
### Color
Use the `color` prop to change the color of the Banner.
```vue
```
### Close
Use the `close` prop to display a [Button](https://ui.nuxt.com/docs/components/button) to dismiss the Banner. Defaults to `false`.
::tip
A `close` event will be emitted when the close button is clicked.
::
```vue [BannerExample.vue]
```
::note
When closed, `banner-${id}` will be stored in the local storage to prevent it from being displayed again. :br For the example above, `banner-example` will be stored in the local storage.
::
### Close Icon
Use the `close-icon` prop to customize the close button [Icon](https://ui.nuxt.com/docs/components/icon). Defaults to `i-lucide-x`.
```vue [BannerExample.vue]
```
::framework-only
#nuxt
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/nuxt#theme
---
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
:::
#vue
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/vue#theme
---
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
:::
::
### Actions
Use the `actions` prop to add some [Button](https://ui.nuxt.com/docs/components/button) actions to the Banner.
```vue
```
::note
The action buttons default to `color="neutral"` and `size="xs"`. You can customize these values by passing them directly to each action button.
::
### Link
You can pass any property from the [``](https://nuxt.com/docs/api/components/nuxt-link){rel="nofollow"} component such as `to`, `target`, `rel`, etc.
```vue
```
::note
The `NuxtLink` component will inherit all other attributes you pass to the `User` component.
::
## Examples
### Within `app.vue`
Use the Banner component in your `app.vue` or in a layout:
```vue [app.vue] {3}
```
## API
### Props
```ts
/**
* Props for the Banner component
*/
interface BannerProps {
/**
* The element or component this component should render as.
*/
as?: any;
/**
* A unique id saved to local storage to remember if the banner has been dismissed.
* Change this value to show the banner again.
*/
id?: string | undefined;
/**
* The icon displayed next to the title.
*/
icon?: string | object | undefined;
title?: string | undefined;
/**
* Display a list of actions next to the title.
* `{ color: 'neutral', size: 'xs' }`{lang="ts-type"}
*/
actions?: ButtonProps[] | undefined;
to?: string | RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric | undefined;
target?: "_blank" | "_parent" | "_self" | "_top" | (string & {}) | null | undefined;
color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined;
/**
* Display a close button to dismiss the banner.
* `{ size: 'md', color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
*/
close?: boolean | Partial | undefined;
/**
* The icon displayed in the close button.
*/
closeIcon?: string | object | undefined;
ui?: { root?: ClassNameValue; container?: ClassNameValue; left?: ClassNameValue; center?: ClassNameValue; right?: ClassNameValue; icon?: ClassNameValue; title?: ClassNameValue; actions?: ClassNameValue; close?: ClassNameValue; } | undefined;
}
```
### Slots
```ts
/**
* Slots for the Banner component
*/
interface BannerSlots {
leading(): any;
title(): any;
actions(): any;
close(): any;
}
```
### Emits
```ts
/**
* Emitted events for the Banner component
*/
interface BannerEmits {
close: (payload: []) => void;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
banner: {
slots: {
root: [
'relative z-50 w-full',
'transition-colors'
],
container: 'flex items-center justify-between gap-3 h-12',
left: 'hidden lg:flex-1 lg:flex lg:items-center',
center: 'flex items-center gap-1.5 min-w-0',
right: 'lg:flex-1 flex items-center justify-end',
icon: 'size-5 shrink-0 text-inverted pointer-events-none',
title: 'text-sm text-inverted font-medium truncate',
actions: 'flex gap-1.5 shrink-0 isolate',
close: 'text-inverted hover:bg-default/10 focus-visible:bg-default/10 -me-1.5 lg:me-0'
},
variants: {
color: {
primary: {
root: 'bg-primary'
},
secondary: {
root: 'bg-secondary'
},
success: {
root: 'bg-success'
},
info: {
root: 'bg-info'
},
warning: {
root: 'bg-warning'
},
error: {
root: 'bg-error'
},
neutral: {
root: 'bg-inverted'
}
},
to: {
true: ''
}
},
compoundVariants: [
{
color: 'primary',
to: true,
class: {
root: 'hover:bg-primary/90'
}
},
{
color: 'secondary',
to: true,
class: {
root: 'hover:bg-secondary/90'
}
},
{
color: 'success',
to: true,
class: {
root: 'hover:bg-success/90'
}
},
{
color: 'info',
to: true,
class: {
root: 'hover:bg-info/90'
}
},
{
color: 'warning',
to: true,
class: {
root: 'hover:bg-warning/90'
}
},
{
color: 'error',
to: true,
class: {
root: 'hover:bg-error/90'
}
},
{
color: 'neutral',
to: true,
class: {
root: 'hover:bg-inverted/90'
}
}
],
defaultVariants: {
color: 'primary'
}
}
}
})
```
## Changelog
::component-changelog
::
# BlogPost
## Usage
The BlogPost component provides a flexible way to display an `` element with customizable content including title, description, image, etc.
::code-preview
:::u-blog-post
---
authors:
- name: Anthony Fu
description: antfu7
avatar:
src: https://github.com/antfu.png
to: https://github.com/antfu
target: _blank
class: w-96
date: 2024-11-25
description: Discover Nuxt Icon v1 - a modern, versatile, and customizable icon
solution for your Nuxt projects.
image: https://nuxt.com/assets/blog/nuxt-icon/cover.png
target: _blank
title: Introducing Nuxt Icon v1
to: https://nuxt.com/blog/nuxt-icon-v1-0
---
:::
::
::tip{to="https://ui.nuxt.com/docs/components/blog-posts"}
Use the [`BlogPosts`](https://ui.nuxt.com/docs/components/blog-posts) component to display multiple blog posts in a responsive grid layout.
::
### Title
Use the `title` prop to display the title of the BlogPost.
```vue
```
### Description
Use the `description` prop to display the description of the BlogPost.
```vue
```
### Date
Use the `date` prop to display the date of the BlogPost.
::tip
The date is automatically formatted to the [current locale](https://ui.nuxt.com/docs/getting-started/integrations/i18n/nuxt#locale). You can either pass a `Date` object or a string.
::
```vue
```
### Badge
Use the `badge` prop to display a [Badge](https://ui.nuxt.com/docs/components/badge) in the BlogPost.
```vue
```
You can pass any property from the [Badge](https://ui.nuxt.com/docs/components/badge#props) component to customize it.
```vue
```
### Image
Use the `image` prop to display an image in the BlogPost.
::note
If [`@nuxt/image`](https://image.nuxt.com/get-started/installation){rel="nofollow"} is installed, the `` component will be used instead of the native `img` tag.
::
```vue
```
### Authors
Use the `authors` prop to display a list of [User](https://ui.nuxt.com/docs/components/user) in the BlogPost as an array of objects with the following properties:
- `name?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `description?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `avatar?: Omit`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `chip?: boolean | Omit`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `size?: UserProps['size']`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `orientation?: UserProps['orientation']`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
You can pass any property from the [Link](https://ui.nuxt.com/docs/components/link#props) component such as `to`, `target`, etc.
```vue
```
When the `authors` prop has more than one item, the [AvatarGroup](https://ui.nuxt.com/docs/components/avatar-group) component is used.
```vue
```
### Link
You can pass any property from the [``](https://nuxt.com/docs/api/components/nuxt-link){rel="nofollow"} component such as `to`, `target`, `rel`, etc.
```vue
```
### Variant
Use the `variant` prop to change the style of the BlogPost.
```vue
```
::note
The styling will be different wether you provide a `to` prop or an `image`.
::
### Orientation
Use the `orientation` prop to change the BlogPost orientation. Defaults to `vertical`.
```vue
```
## API
### Props
```ts
/**
* Props for the BlogPost component
*/
interface BlogPostProps {
/**
* The element or component this component should render as.
* @default "\"article\""
*/
as?: any;
title?: string | undefined;
description?: string | undefined;
/**
* The date of the blog post. Can be a string or a Date object.
*/
date?: string | Date | undefined;
/**
* Display a badge on the blog post.
* Can be a string or an object.
* `{ color: 'neutral', variant: 'subtle' }`{lang="ts-type"}
*/
badge?: string | BadgeProps | undefined;
/**
* The authors of the blog post.
*/
authors?: UserProps[] | undefined;
/**
* The image of the blog post. Can be a string or an object.
*/
image?: string | (Partial & { [key: string]: any; }) | undefined;
/**
* The orientation of the blog post.
* @default "\"vertical\""
*/
orientation?: "vertical" | "horizontal" | undefined;
variant?: "outline" | "soft" | "subtle" | "ghost" | "naked" | undefined;
to?: string | RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric | undefined;
target?: "_blank" | "_parent" | "_self" | "_top" | (string & {}) | null | undefined;
onClick?: ((event: MouseEvent) => void | Promise) | undefined;
ui?: { root?: ClassNameValue; header?: ClassNameValue; body?: ClassNameValue; footer?: ClassNameValue; image?: ClassNameValue; title?: ClassNameValue; description?: ClassNameValue; authors?: ClassNameValue; avatar?: ClassNameValue; meta?: ClassNameValue; date?: ClassNameValue; badge?: ClassNameValue; } | undefined;
}
```
### Slots
```ts
/**
* Slots for the BlogPost component
*/
interface BlogPostSlots {
date(): any;
badge(): any;
title(): any;
description(): any;
authors(): any;
header(): any;
body(): any;
footer(): any;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
blogPost: {
slots: {
root: 'relative group/blog-post flex flex-col rounded-lg overflow-hidden',
header: 'relative overflow-hidden aspect-[16/9] w-full pointer-events-none',
body: 'min-w-0 flex-1 flex flex-col',
footer: '',
image: 'object-cover object-top w-full h-full',
title: 'text-xl text-pretty font-semibold text-highlighted',
description: 'mt-1 text-base text-pretty',
authors: 'pt-4 mt-auto flex flex-wrap gap-x-3 gap-y-1.5',
avatar: '',
meta: 'flex items-center gap-2 mb-2',
date: 'text-sm',
badge: ''
},
variants: {
orientation: {
horizontal: {
root: 'lg:grid lg:grid-cols-2 lg:items-center gap-x-8',
body: 'justify-center p-4 sm:p-6 lg:px-0'
},
vertical: {
root: 'flex flex-col',
body: 'p-4 sm:p-6'
}
},
variant: {
outline: {
root: 'bg-default ring ring-default',
date: 'text-toned',
description: 'text-muted'
},
soft: {
root: 'bg-elevated/50',
date: 'text-muted',
description: 'text-toned'
},
subtle: {
root: 'bg-elevated/50 ring ring-default',
date: 'text-muted',
description: 'text-toned'
},
ghost: {
date: 'text-toned',
description: 'text-muted',
header: 'shadow-lg rounded-lg'
},
naked: {
root: 'p-0 sm:p-0',
date: 'text-toned',
description: 'text-muted',
header: 'shadow-lg rounded-lg'
}
},
to: {
true: {
root: [
'transition'
],
image: 'transform transition-transform duration-200 group-hover/blog-post:scale-110',
avatar: 'transform transition-transform duration-200 hover:scale-115'
}
},
image: {
true: ''
}
},
compoundVariants: [
{
variant: 'outline',
to: true,
class: {
root: 'hover:bg-elevated/50'
}
},
{
variant: 'soft',
to: true,
class: {
root: 'hover:bg-elevated'
}
},
{
variant: 'subtle',
to: true,
class: {
root: 'hover:bg-elevated hover:ring-accented'
}
},
{
variant: 'ghost',
to: true,
class: {
root: 'hover:bg-elevated/50',
header: [
'group-hover/blog-post:shadow-none',
'transition-all'
]
}
},
{
variant: 'ghost',
to: true,
orientation: 'vertical',
class: {
header: 'group-hover/blog-post:rounded-b-none'
}
},
{
variant: 'ghost',
to: true,
orientation: 'horizontal',
class: {
header: 'group-hover/blog-post:rounded-r-none'
}
},
{
orientation: 'vertical',
image: false,
variant: 'naked',
class: {
body: 'p-0 sm:p-0'
}
}
],
defaultVariants: {
variant: 'outline'
}
}
}
})
```
## Changelog
::component-changelog
::
# BlogPosts
## Usage
The BlogPosts component provides a flexible layout to display a list of [BlogPost](https://ui.nuxt.com/docs/components/blog-post) components using either the default slot or the `posts` prop.
```vue {2,8}
```
### Posts
Use the `posts` prop as an array of objects with the properties of the [BlogPost](https://ui.nuxt.com/docs/components/blog-post#props) component.
```vue
```
### Orientation
Use the `orientation` prop to change the orientation of the BlogPosts. Defaults to `horizontal`.
```vue
```
::tip
When using the `posts` prop instead of the default slot, the `orientation` of the posts is automatically reversed, `horizontal` to `vertical` and vice versa.
::
## Examples
::note
While these examples use [Nuxt Content](https://content.nuxt.com){rel="nofollow"}, the components can be integrated with any content management system.
::
### Within a page
Use the BlogPosts component in a page to create a blog page:
```vue [pages/blog/index.vue] {11-18}
```
::note
In this example, the `posts` are fetched using `queryCollection` from the `@nuxt/content` module.
::
::tip
The `to` prop is overridden here since `@nuxt/content` uses the `path` property.
::
## API
### Props
```ts
/**
* Props for the BlogPosts component
*/
interface BlogPostsProps {
/**
* The element or component this component should render as.
*/
as?: any;
posts?: BlogPostProps[] | undefined;
/**
* The orientation of the blog posts.
* @default "\"horizontal\""
*/
orientation?: "horizontal" | "vertical" | undefined;
}
```
### Slots
```ts
/**
* Slots for the BlogPosts component
*/
interface BlogPostsSlots {
date(): any;
badge(): any;
title(): any;
description(): any;
authors(): any;
header(): any;
body(): any;
footer(): any;
default(): any;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
blogPosts: {
base: 'flex flex-col gap-8 lg:gap-y-16',
variants: {
orientation: {
horizontal: 'sm:grid sm:grid-cols-2 lg:grid-cols-3',
vertical: ''
}
}
}
}
})
```
## Changelog
::component-changelog
::
# Breadcrumb
## Usage
Use the Breadcrumb component to show the current page's location in your site's hierarchy.
```vue
```
### Items
Use the `items` prop as an array of objects with the following properties:
- `label?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `icon?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `avatar?: AvatarProps`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- [`slot?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}](https://ui.nuxt.com/#with-custom-slot)
- `class?: any`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `ui?: { item?: ClassNameValue, link?: ClassNameValue, linkLeadingIcon?: ClassNameValue, linkLeadingAvatar?: ClassNameValue, linkLabel?: ClassNameValue, separator?: ClassNameValue, separatorIcon?: ClassNameValue }`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
You can pass any property from the [Link](https://ui.nuxt.com/docs/components/link#props) component such as `to`, `target`, etc.
```vue
```
::note
A `span` is rendered instead of a link when the `to` property is not defined.
::
### Separator Icon
Use the `separator-icon` prop to customize the [Icon](https://ui.nuxt.com/docs/components/icon) between each item. Defaults to `i-lucide-chevron-right`.
```vue
```
::framework-only
#nuxt
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/nuxt#theme
---
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronRight` key.
:::
#vue
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/vue#theme
---
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronRight` key.
:::
::
## Examples
### With separator slot
Use the `#separator` slot to customize the separator between each item.
```vue [BreadcrumbSeparatorSlotExample.vue]
/
```
### With custom slot
Use the `slot` property to customize a specific item.
You will have access to the following slots:
- `#{{ item.slot }}`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `#{{ item.slot }}-leading`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `#{{ item.slot }}-label`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `#{{ item.slot }}-trailing`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```vue [BreadcrumbCustomSlotExample.vue]
```
::tip{to="https://ui.nuxt.com/#slots"}
You can also use the `#item`, `#item-leading`, `#item-label` and `#item-trailing` slots to customize all items.
::
## API
### Props
```ts
/**
* Props for the Breadcrumb component
*/
interface BreadcrumbProps {
/**
* The element or component this component should render as.
* @default "\"nav\""
*/
as?: any;
items?: BreadcrumbItem[] | undefined;
/**
* The icon to use as a separator.
*/
separatorIcon?: string | object | undefined;
/**
* The key used to get the label from the item.
* @default "\"label\""
*/
labelKey?: GetItemKeys | undefined;
ui?: { root?: ClassNameValue; list?: ClassNameValue; item?: ClassNameValue; link?: ClassNameValue; linkLeadingIcon?: ClassNameValue; linkLeadingAvatar?: ClassNameValue; linkLeadingAvatarSize?: ClassNameValue; linkLabel?: ClassNameValue; separator?: ClassNameValue; separatorIcon?: ClassNameValue; } | undefined;
}
```
### Slots
```ts
/**
* Slots for the Breadcrumb component
*/
interface BreadcrumbSlots {
item(): any;
item-leading(): any;
item-label(): any;
item-trailing(): any;
separator(): any;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
breadcrumb: {
slots: {
root: 'relative min-w-0',
list: 'flex items-center gap-1.5',
item: 'flex min-w-0',
link: 'group relative flex items-center gap-1.5 text-sm min-w-0 focus-visible:outline-primary',
linkLeadingIcon: 'shrink-0 size-5',
linkLeadingAvatar: 'shrink-0',
linkLeadingAvatarSize: '2xs',
linkLabel: 'truncate',
separator: 'flex',
separatorIcon: 'shrink-0 size-5 text-muted'
},
variants: {
active: {
true: {
link: 'text-primary font-semibold'
},
false: {
link: 'text-muted font-medium'
}
},
disabled: {
true: {
link: 'cursor-not-allowed opacity-75'
}
},
to: {
true: ''
}
},
compoundVariants: [
{
disabled: false,
active: false,
to: true,
class: {
link: [
'hover:text-default',
'transition-colors'
]
}
}
]
}
}
})
```
## Changelog
::component-changelog
::
# Button
## Usage
Use the default slot to set the label of the Button.
```vue
Button
```
### Label
Use the `label` prop to set the label of the Button.
```vue
```
### Color
Use the `color` prop to change the color of the Button.
```vue
Button
```
### Variant
Use the `variant` prop to change the variant of the Button.
```vue
Button
```
### Size
Use the `size` prop to change the size of the Button.
```vue
Button
```
### Icon
Use the `icon` prop to show an [Icon](https://ui.nuxt.com/docs/components/icon) inside the Button.
```vue
Button
```
Use the `leading` and `trailing` props to set the icon position or the `leading-icon` and `trailing-icon` props to set a different icon for each position.
```vue
Button
```
The `label` as prop or slot is optional so you can use the Button as an icon-only button.
```vue
```
### Avatar
Use the `avatar` prop to show an [Avatar](https://ui.nuxt.com/docs/components/avatar) inside the Button.
```vue
Button
```
The `label` as prop or slot is optional so you can use the Button as an avatar-only button.
```vue
```
### Link
You can pass any property from the [Link](https://ui.nuxt.com/docs/components/link#props) component such as `to`, `target`, etc.
```vue
Button
```
When the Button is a link or when using the `active` prop, you can use the `active-color` and `active-variant` props to customize the active state.
```vue
Button
```
You can also use the `active-class` and `inactive-class` props to customize the active state.
```vue
Button
```
::tip
You can configure these styles globally in your `app.config.ts` file under the `ui.button.variants.active` key.
```ts
export default defineAppConfig({
ui: {
button: {
variants: {
active: {
true: {
base: 'font-bold'
}
}
}
}
}
})
```
::
### Loading
Use the `loading` prop to show a loading icon and disable the Button.
```vue
Button
```
Use the `loading-auto` prop to show the loading icon automatically while the `@click` promise is pending.
```vue [ButtonLoadingAutoExample.vue]
Button
```
This also works with the [Form](https://ui.nuxt.com/docs/components/form) component.
```vue [ButtonLoadingAutoFormExample.vue]
Submit
```
### Loading Icon
Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-loader-circle`.
```vue
Button
```
::framework-only
#nuxt
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/nuxt#theme
---
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
:::
#vue
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/vue#theme
---
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
:::
::
### Disabled
Use the `disabled` prop to disable the Button.
```vue
Button
```
## Examples
### `class` prop
Use the `class` prop to override the base styles of the Button.
```vue
Button
```
### `ui` prop
Use the `ui` prop to override the slots styles of the Button.
```vue
Button
```
## API
### Props
```ts
/**
* Props for the Button component
*/
interface ButtonProps {
label?: string | undefined;
color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined;
activeColor?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined;
variant?: "solid" | "outline" | "soft" | "subtle" | "ghost" | "link" | undefined;
activeVariant?: "solid" | "outline" | "soft" | "subtle" | "ghost" | "link" | undefined;
size?: "xs" | "sm" | "md" | "lg" | "xl" | undefined;
/**
* Render the button with equal padding on all sides.
*/
square?: boolean | undefined;
/**
* Render the button full width.
*/
block?: boolean | undefined;
/**
* Set loading state automatically based on the `@click` promise state
*/
loadingAuto?: boolean | undefined;
onClick?: ((event: MouseEvent) => void | Promise) | ((event: MouseEvent) => void | Promise)[] | undefined;
ui?: { base?: ClassNameValue; label?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; trailingIcon?: ClassNameValue; } | undefined;
/**
* Display an icon based on the `leading` and `trailing` props.
*/
icon?: string | object | undefined;
/**
* Display an avatar on the left side.
*/
avatar?: AvatarProps | undefined;
/**
* When `true`, the icon will be displayed on the left side.
*/
leading?: boolean | undefined;
/**
* Display an icon on the left side.
*/
leadingIcon?: string | object | undefined;
/**
* When `true`, the icon will be displayed on the right side.
*/
trailing?: boolean | undefined;
/**
* Display an icon on the right side.
*/
trailingIcon?: string | object | undefined;
/**
* When `true`, the loading icon will be displayed.
*/
loading?: boolean | undefined;
/**
* The icon when the `loading` prop is `true`.
*/
loadingIcon?: string | object | undefined;
/**
* Route Location the link should navigate to when clicked on.
*/
to?: string | RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric | undefined;
/**
* Class to apply when the link is active
*/
activeClass?: string | undefined;
/**
* Class to apply when the link is exact active
*/
exactActiveClass?: string | undefined;
/**
* Value passed to the attribute `aria-current` when the link is exact active.
*/
ariaCurrentValue?: "page" | "step" | "location" | "date" | "time" | "true" | "false" | undefined;
/**
* Pass the returned promise of `router.push()` to `document.startViewTransition()` if supported.
*/
viewTransition?: boolean | undefined;
/**
* Calls `router.replace` instead of `router.push`.
*/
replace?: boolean | undefined;
/**
* The element or component this component should render as when not a link.
*/
as?: any;
/**
* The type of the button when not a link.
*/
type?: "reset" | "submit" | "button" | undefined;
disabled?: boolean | undefined;
/**
* Force the link to be active independent of the current route.
*/
active?: boolean | undefined;
/**
* Will only be active if the current route is an exact match.
*/
exact?: boolean | undefined;
/**
* Allows controlling how the current route query sets the link as active.
*/
exactQuery?: boolean | "partial" | undefined;
/**
* Will only be active if the current route hash is an exact match.
*/
exactHash?: boolean | undefined;
/**
* The class to apply when the link is inactive.
*/
inactiveClass?: string | undefined;
/**
* An alias for `to`. If used with `to`, `href` will be ignored
*/
href?: string | RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric | undefined;
/**
* Forces the link to be considered as external (true) or internal (false). This is helpful to handle edge-cases
*/
external?: boolean | undefined;
/**
* Where to display the linked URL, as the name for a browsing context.
*/
target?: "_blank" | "_parent" | "_self" | "_top" | (string & {}) | null | undefined;
/**
* A rel attribute value to apply on the link. Defaults to "noopener noreferrer" for external links.
*/
rel?: (string & {}) | "noopener" | "noreferrer" | "nofollow" | "sponsored" | "ugc" | null | undefined;
/**
* If set to true, no rel attribute will be added to the link
*/
noRel?: boolean | undefined;
/**
* A class to apply to links that have been prefetched.
*/
prefetchedClass?: string | undefined;
/**
* When enabled will prefetch middleware, layouts and payloads of links in the viewport.
*/
prefetch?: boolean | undefined;
/**
* Allows controlling when to prefetch links. By default, prefetch is triggered only on visibility.
*/
prefetchOn?: "visibility" | "interaction" | Partial<{ visibility: boolean; interaction: boolean; }> | undefined;
/**
* Escape hatch to disable `prefetch` attribute.
*/
noPrefetch?: boolean | undefined;
}
```
::callout
---
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/v4/src/runtime/components/Link.vue#L13
---
The `Button` component extends the `Link` component. Check out the source code on GitHub.
::
### Slots
```ts
/**
* Slots for the Button component
*/
interface ButtonSlots {
leading(): any;
default(): any;
trailing(): any;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
button: {
slots: {
base: [
'rounded-md font-medium inline-flex items-center disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75',
'transition-colors'
],
label: 'truncate',
leadingIcon: 'shrink-0',
leadingAvatar: 'shrink-0',
leadingAvatarSize: '',
trailingIcon: 'shrink-0'
},
variants: {
fieldGroup: {
horizontal: 'not-only:first:rounded-e-none not-only:last:rounded-s-none not-last:not-first:rounded-none focus-visible:z-[1]',
vertical: 'not-only:first:rounded-b-none not-only:last:rounded-t-none not-last:not-first:rounded-none focus-visible:z-[1]'
},
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
variant: {
solid: '',
outline: '',
soft: '',
subtle: '',
ghost: '',
link: ''
},
size: {
xs: {
base: 'px-2 py-1 text-xs gap-1',
leadingIcon: 'size-4',
leadingAvatarSize: '3xs',
trailingIcon: 'size-4'
},
sm: {
base: 'px-2.5 py-1.5 text-xs gap-1.5',
leadingIcon: 'size-4',
leadingAvatarSize: '3xs',
trailingIcon: 'size-4'
},
md: {
base: 'px-2.5 py-1.5 text-sm gap-1.5',
leadingIcon: 'size-5',
leadingAvatarSize: '2xs',
trailingIcon: 'size-5'
},
lg: {
base: 'px-3 py-2 text-sm gap-2',
leadingIcon: 'size-5',
leadingAvatarSize: '2xs',
trailingIcon: 'size-5'
},
xl: {
base: 'px-3 py-2 text-base gap-2',
leadingIcon: 'size-6',
leadingAvatarSize: 'xs',
trailingIcon: 'size-6'
}
},
block: {
true: {
base: 'w-full justify-center',
trailingIcon: 'ms-auto'
}
},
square: {
true: ''
},
leading: {
true: ''
},
trailing: {
true: ''
},
loading: {
true: ''
},
active: {
true: {
base: ''
},
false: {
base: ''
}
}
},
compoundVariants: [
{
color: 'primary',
variant: 'solid',
class: 'text-inverted bg-primary hover:bg-primary/75 active:bg-primary/75 disabled:bg-primary aria-disabled:bg-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary'
},
{
color: 'secondary',
variant: 'solid',
class: 'text-inverted bg-secondary hover:bg-secondary/75 active:bg-secondary/75 disabled:bg-secondary aria-disabled:bg-secondary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary'
},
{
color: 'success',
variant: 'solid',
class: 'text-inverted bg-success hover:bg-success/75 active:bg-success/75 disabled:bg-success aria-disabled:bg-success focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-success'
},
{
color: 'info',
variant: 'solid',
class: 'text-inverted bg-info hover:bg-info/75 active:bg-info/75 disabled:bg-info aria-disabled:bg-info focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-info'
},
{
color: 'warning',
variant: 'solid',
class: 'text-inverted bg-warning hover:bg-warning/75 active:bg-warning/75 disabled:bg-warning aria-disabled:bg-warning focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-warning'
},
{
color: 'error',
variant: 'solid',
class: 'text-inverted bg-error hover:bg-error/75 active:bg-error/75 disabled:bg-error aria-disabled:bg-error focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-error'
},
{
color: 'primary',
variant: 'outline',
class: 'ring ring-inset ring-primary/50 text-primary hover:bg-primary/10 active:bg-primary/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus:outline-none focus-visible:ring-2 focus-visible:ring-primary'
},
{
color: 'secondary',
variant: 'outline',
class: 'ring ring-inset ring-secondary/50 text-secondary hover:bg-secondary/10 active:bg-secondary/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus:outline-none focus-visible:ring-2 focus-visible:ring-secondary'
},
{
color: 'success',
variant: 'outline',
class: 'ring ring-inset ring-success/50 text-success hover:bg-success/10 active:bg-success/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus:outline-none focus-visible:ring-2 focus-visible:ring-success'
},
{
color: 'info',
variant: 'outline',
class: 'ring ring-inset ring-info/50 text-info hover:bg-info/10 active:bg-info/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus:outline-none focus-visible:ring-2 focus-visible:ring-info'
},
{
color: 'warning',
variant: 'outline',
class: 'ring ring-inset ring-warning/50 text-warning hover:bg-warning/10 active:bg-warning/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus:outline-none focus-visible:ring-2 focus-visible:ring-warning'
},
{
color: 'error',
variant: 'outline',
class: 'ring ring-inset ring-error/50 text-error hover:bg-error/10 active:bg-error/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent focus:outline-none focus-visible:ring-2 focus-visible:ring-error'
},
{
color: 'primary',
variant: 'soft',
class: 'text-primary bg-primary/10 hover:bg-primary/15 active:bg-primary/15 focus:outline-none focus-visible:bg-primary/15 disabled:bg-primary/10 aria-disabled:bg-primary/10'
},
{
color: 'secondary',
variant: 'soft',
class: 'text-secondary bg-secondary/10 hover:bg-secondary/15 active:bg-secondary/15 focus:outline-none focus-visible:bg-secondary/15 disabled:bg-secondary/10 aria-disabled:bg-secondary/10'
},
{
color: 'success',
variant: 'soft',
class: 'text-success bg-success/10 hover:bg-success/15 active:bg-success/15 focus:outline-none focus-visible:bg-success/15 disabled:bg-success/10 aria-disabled:bg-success/10'
},
{
color: 'info',
variant: 'soft',
class: 'text-info bg-info/10 hover:bg-info/15 active:bg-info/15 focus:outline-none focus-visible:bg-info/15 disabled:bg-info/10 aria-disabled:bg-info/10'
},
{
color: 'warning',
variant: 'soft',
class: 'text-warning bg-warning/10 hover:bg-warning/15 active:bg-warning/15 focus:outline-none focus-visible:bg-warning/15 disabled:bg-warning/10 aria-disabled:bg-warning/10'
},
{
color: 'error',
variant: 'soft',
class: 'text-error bg-error/10 hover:bg-error/15 active:bg-error/15 focus:outline-none focus-visible:bg-error/15 disabled:bg-error/10 aria-disabled:bg-error/10'
},
{
color: 'primary',
variant: 'subtle',
class: 'text-primary ring ring-inset ring-primary/25 bg-primary/10 hover:bg-primary/15 active:bg-primary/15 disabled:bg-primary/10 aria-disabled:bg-primary/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary'
},
{
color: 'secondary',
variant: 'subtle',
class: 'text-secondary ring ring-inset ring-secondary/25 bg-secondary/10 hover:bg-secondary/15 active:bg-secondary/15 disabled:bg-secondary/10 aria-disabled:bg-secondary/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-secondary'
},
{
color: 'success',
variant: 'subtle',
class: 'text-success ring ring-inset ring-success/25 bg-success/10 hover:bg-success/15 active:bg-success/15 disabled:bg-success/10 aria-disabled:bg-success/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-success'
},
{
color: 'info',
variant: 'subtle',
class: 'text-info ring ring-inset ring-info/25 bg-info/10 hover:bg-info/15 active:bg-info/15 disabled:bg-info/10 aria-disabled:bg-info/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-info'
},
{
color: 'warning',
variant: 'subtle',
class: 'text-warning ring ring-inset ring-warning/25 bg-warning/10 hover:bg-warning/15 active:bg-warning/15 disabled:bg-warning/10 aria-disabled:bg-warning/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-warning'
},
{
color: 'error',
variant: 'subtle',
class: 'text-error ring ring-inset ring-error/25 bg-error/10 hover:bg-error/15 active:bg-error/15 disabled:bg-error/10 aria-disabled:bg-error/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-error'
},
{
color: 'primary',
variant: 'ghost',
class: 'text-primary hover:bg-primary/10 active:bg-primary/10 focus:outline-none focus-visible:bg-primary/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent'
},
{
color: 'secondary',
variant: 'ghost',
class: 'text-secondary hover:bg-secondary/10 active:bg-secondary/10 focus:outline-none focus-visible:bg-secondary/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent'
},
{
color: 'success',
variant: 'ghost',
class: 'text-success hover:bg-success/10 active:bg-success/10 focus:outline-none focus-visible:bg-success/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent'
},
{
color: 'info',
variant: 'ghost',
class: 'text-info hover:bg-info/10 active:bg-info/10 focus:outline-none focus-visible:bg-info/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent'
},
{
color: 'warning',
variant: 'ghost',
class: 'text-warning hover:bg-warning/10 active:bg-warning/10 focus:outline-none focus-visible:bg-warning/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent'
},
{
color: 'error',
variant: 'ghost',
class: 'text-error hover:bg-error/10 active:bg-error/10 focus:outline-none focus-visible:bg-error/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent'
},
{
color: 'primary',
variant: 'link',
class: 'text-primary hover:text-primary/75 active:text-primary/75 disabled:text-primary aria-disabled:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary'
},
{
color: 'secondary',
variant: 'link',
class: 'text-secondary hover:text-secondary/75 active:text-secondary/75 disabled:text-secondary aria-disabled:text-secondary focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-secondary'
},
{
color: 'success',
variant: 'link',
class: 'text-success hover:text-success/75 active:text-success/75 disabled:text-success aria-disabled:text-success focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-success'
},
{
color: 'info',
variant: 'link',
class: 'text-info hover:text-info/75 active:text-info/75 disabled:text-info aria-disabled:text-info focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-info'
},
{
color: 'warning',
variant: 'link',
class: 'text-warning hover:text-warning/75 active:text-warning/75 disabled:text-warning aria-disabled:text-warning focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-warning'
},
{
color: 'error',
variant: 'link',
class: 'text-error hover:text-error/75 active:text-error/75 disabled:text-error aria-disabled:text-error focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-error'
},
{
color: 'neutral',
variant: 'solid',
class: 'text-inverted bg-inverted hover:bg-inverted/90 active:bg-inverted/90 disabled:bg-inverted aria-disabled:bg-inverted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-inverted'
},
{
color: 'neutral',
variant: 'outline',
class: 'ring ring-inset ring-accented text-default bg-default hover:bg-elevated active:bg-elevated disabled:bg-default aria-disabled:bg-default focus:outline-none focus-visible:ring-2 focus-visible:ring-inverted'
},
{
color: 'neutral',
variant: 'soft',
class: 'text-default bg-elevated hover:bg-accented/75 active:bg-accented/75 focus:outline-none focus-visible:bg-accented/75 disabled:bg-elevated aria-disabled:bg-elevated'
},
{
color: 'neutral',
variant: 'subtle',
class: 'ring ring-inset ring-accented text-default bg-elevated hover:bg-accented/75 active:bg-accented/75 disabled:bg-elevated aria-disabled:bg-elevated focus:outline-none focus-visible:ring-2 focus-visible:ring-inverted'
},
{
color: 'neutral',
variant: 'ghost',
class: 'text-default hover:bg-elevated active:bg-elevated focus:outline-none focus-visible:bg-elevated hover:disabled:bg-transparent dark:hover:disabled:bg-transparent hover:aria-disabled:bg-transparent dark:hover:aria-disabled:bg-transparent'
},
{
color: 'neutral',
variant: 'link',
class: 'text-muted hover:text-default active:text-default disabled:text-muted aria-disabled:text-muted focus:outline-none focus-visible:ring-inset focus-visible:ring-2 focus-visible:ring-inverted'
},
{
size: 'xs',
square: true,
class: 'p-1'
},
{
size: 'sm',
square: true,
class: 'p-1.5'
},
{
size: 'md',
square: true,
class: 'p-1.5'
},
{
size: 'lg',
square: true,
class: 'p-2'
},
{
size: 'xl',
square: true,
class: 'p-2'
},
{
loading: true,
leading: true,
class: {
leadingIcon: 'animate-spin'
}
},
{
loading: true,
leading: false,
trailing: true,
class: {
trailingIcon: 'animate-spin'
}
}
],
defaultVariants: {
color: 'primary',
variant: 'solid',
size: 'md'
}
}
}
})
```
## Changelog
::component-changelog
::
# Calendar
## Usage
Use the `v-model` directive to control the selected date.
```vue
```
Use the `default-value` prop to set the initial value when you do not need to control its state.
```vue
```
::note
This component relies on the [`@internationalized/date`](https://react-spectrum.adobe.com/internationalized/date/index.html){rel="nofollow"} package which provides objects and functions for representing and manipulating dates and times in a locale-aware manner.
::
### Multiple
Use the `multiple` prop to allow multiple selections.
```vue
```
### Range
Use the `range` prop to select a range of dates.
```vue
```
### Color
Use the `color` prop to change the color of the calendar.
```vue
```
### Variant
Use the `variant` prop to change the variant of the calendar.
```vue
```
### Size
Use the `size` prop to change the size of the calendar.
```vue
```
### Disabled
Use the `disabled` prop to disable the calendar.
```vue
```
### Number Of Months
Use the `numberOfMonths` prop to change the number of months in the calendar.
```vue
```
### Month Controls
Use the `month-controls` prop to show the month controls. Defaults to `true`.
```vue
```
### Year Controls
Use the `year-controls` prop to show the year controls. Defaults to `true`.
```vue
```
### Fixed Weeks
Use the `fixed-weeks` prop to display the calendar with fixed weeks.
```vue
```
## Examples
### With chip events
Use the [Chip](https://ui.nuxt.com/docs/components/chip) component to add events to specific days.
```vue [CalendarEventsExample.vue]
{{ day.day }}
```
### With disabled dates
Use the `is-date-disabled` prop with a function to mark specific dates as disabled.
```vue [CalendarDisabledDatesExample.vue]
```
### With unavailable dates
Use the `is-date-unavailable` prop with a function to mark specific dates as unavailable.
```vue [CalendarUnavailableDatesExample.vue]
```
### With min/max dates
Use the `min-value` and `max-value` props to limit the dates.
```vue [CalendarMinMaxDatesExample.vue]
```
### With other calendar systems
You can use other calenders from `@internationalized/date` to implement a different calendar system.
```vue [CalendarOtherSystemExample.vue]
```
::note
---
to: https://react-spectrum.adobe.com/internationalized/date/Calendar.html#implementations
---
You can check all the available calendars on `@internationalized/date` docs.
::
### With external controls
You can control the calendar with external controls by manipulating the date passed in the `v-model`.
```vue [CalendarExternalControlsExample.vue]
Prev
Next
```
### As a DatePicker
Use a [Button](https://ui.nuxt.com/docs/components/button) and a [Popover](https://ui.nuxt.com/docs/components/popover) component to create a date picker.
```vue [CalendarDatePickerExample.vue]
{{ modelValue ? df.format(modelValue.toDate(getLocalTimeZone())) : 'Select a date' }}
```
### As a DateRangePicker
Use a [Button](https://ui.nuxt.com/docs/components/button) and a [Popover](https://ui.nuxt.com/docs/components/popover) component to create a date range picker.
```vue [CalendarDateRangePickerExample.vue]
{{ df.format(modelValue.start.toDate(getLocalTimeZone())) }} - {{ df.format(modelValue.end.toDate(getLocalTimeZone())) }}
{{ df.format(modelValue.start.toDate(getLocalTimeZone())) }}
Pick a date
```
## API
### Props
```ts
/**
* Props for the Calendar component
*/
interface CalendarProps {
/**
* The element or component this component should render as.
*/
as?: any;
/**
* The icon to use for the next year control.
*/
nextYearIcon?: string | object | undefined;
/**
* Configure the next year button.
* `{ color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
*/
nextYear?: ButtonProps | undefined;
/**
* The icon to use for the next month control.
*/
nextMonthIcon?: string | object | undefined;
/**
* Configure the next month button.
* `{ color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
*/
nextMonth?: ButtonProps | undefined;
/**
* The icon to use for the previous year control.
*/
prevYearIcon?: string | object | undefined;
/**
* Configure the prev year button.
* `{ color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
*/
prevYear?: ButtonProps | undefined;
/**
* The icon to use for the previous month control.
*/
prevMonthIcon?: string | object | undefined;
/**
* Configure the prev month button.
* `{ color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
*/
prevMonth?: ButtonProps | undefined;
color?: "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral" | undefined;
variant?: "solid" | "outline" | "soft" | "subtle" | undefined;
size?: "md" | "xs" | "sm" | "lg" | "xl" | undefined;
/**
* Whether or not a range of dates can be selected
*/
range?: boolean | undefined;
/**
* Whether or not multiple dates can be selected
*/
multiple?: boolean | undefined;
/**
* Show month controls
* @default "true"
*/
monthControls?: boolean | undefined;
/**
* Show year controls
* @default "true"
*/
yearControls?: boolean | undefined;
defaultValue?: DateValue | DateRange | DateValue[] | undefined;
modelValue?: DateValue | DateRange | DateValue[] | null | undefined;
ui?: { root?: ClassNameValue; header?: ClassNameValue; body?: ClassNameValue; heading?: ClassNameValue; grid?: ClassNameValue; gridRow?: ClassNameValue; gridWeekDaysRow?: ClassNameValue; gridBody?: ClassNameValue; headCell?: ClassNameValue; cell?: ClassNameValue; cellTrigger?: ClassNameValue; } | undefined;
/**
* The default placeholder date
*/
defaultPlaceholder?: DateValue | undefined;
/**
* The placeholder date, which is used to determine what month to display when no date is selected. This updates as the user navigates the calendar and can be used to programmatically control the calendar view
*/
placeholder?: DateValue | undefined;
/**
* When combined with `isDateUnavailable`, determines whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected.
*/
allowNonContiguousRanges?: boolean | undefined;
/**
* This property causes the previous and next buttons to navigate by the number of months displayed at once, rather than one month
*/
pagedNavigation?: boolean | undefined;
/**
* Whether or not to prevent the user from deselecting a date without selecting another date first
*/
preventDeselect?: boolean | undefined;
/**
* The maximum number of days that can be selected in a range
*/
maximumDays?: number | undefined;
/**
* The day of the week to start the calendar on
*/
weekStartsOn?: 0 | 1 | 2 | 4 | 5 | 3 | 6 | undefined;
/**
* The format to use for the weekday strings provided via the weekdays slot prop
*/
weekdayFormat?: WeekDayFormat | undefined;
/**
* Whether or not to always display 6 weeks in the calendar
* @default "true"
*/
fixedWeeks?: boolean | undefined;
/**
* The maximum date that can be selected
*/
maxValue?: DateValue | undefined;
/**
* The minimum date that can be selected
*/
minValue?: DateValue | undefined;
/**
* The number of months to display at once
*/
numberOfMonths?: number | undefined;
/**
* Whether or not the calendar is disabled
*/
disabled?: boolean | undefined;
/**
* Whether or not the calendar is readonly
*/
readonly?: boolean | undefined;
/**
* If true, the calendar will focus the selected day, today, or the first day of the month depending on what is visible when the calendar is mounted
*/
initialFocus?: boolean | undefined;
/**
* A function that returns whether or not a date is disabled
*/
isDateDisabled?: Matcher | undefined;
/**
* A function that returns whether or not a date is unavailable
*/
isDateUnavailable?: Matcher | undefined;
/**
* A function that returns whether or not a date is hightable
*/
isDateHighlightable?: Matcher | undefined;
/**
* A function that returns the next page of the calendar. It receives the current placeholder as an argument inside the component.
*/
nextPage?: ((placeholder: DateValue) => DateValue) | undefined;
/**
* A function that returns the previous page of the calendar. It receives the current placeholder as an argument inside the component.
*/
prevPage?: ((placeholder: DateValue) => DateValue) | undefined;
/**
* Whether or not to disable days outside the current view.
*/
disableDaysOutsideCurrentView?: boolean | undefined;
/**
* Which part of the range should be fixed
*/
fixedDate?: "start" | "end" | undefined;
}
```
### Slots
```ts
/**
* Slots for the Calendar component
*/
interface CalendarSlots {
heading(): any;
day(): any;
week-day(): any;
}
```
### Emits
```ts
/**
* Emitted events for the Calendar component
*/
interface CalendarEmits {
update:modelValue: (payload: [date: DateValue | DateRange | DateValue[] | null | undefined]) => void;
update:placeholder: (payload: [date: DateValue] & [date: DateValue]) => void;
update:validModelValue: (payload: [date: DateRange]) => void;
update:startValue: (payload: [date: DateValue | undefined]) => void;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
calendar: {
slots: {
root: '',
header: 'flex items-center justify-between',
body: 'flex flex-col space-y-4 pt-4 sm:flex-row sm:space-x-4 sm:space-y-0',
heading: 'text-center font-medium truncate mx-auto',
grid: 'w-full border-collapse select-none space-y-1 focus:outline-none',
gridRow: 'grid grid-cols-7 place-items-center',
gridWeekDaysRow: 'mb-1 grid w-full grid-cols-7',
gridBody: 'grid',
headCell: 'rounded-md',
cell: 'relative text-center',
cellTrigger: [
'm-0.5 relative flex items-center justify-center rounded-full whitespace-nowrap focus-visible:ring-2 focus:outline-none data-disabled:text-muted data-unavailable:line-through data-unavailable:text-muted data-unavailable:pointer-events-none data-today:font-semibold data-[outside-view]:text-muted',
'transition'
]
},
variants: {
color: {
primary: {
headCell: 'text-primary',
cellTrigger: 'focus-visible:ring-primary'
},
secondary: {
headCell: 'text-secondary',
cellTrigger: 'focus-visible:ring-secondary'
},
success: {
headCell: 'text-success',
cellTrigger: 'focus-visible:ring-success'
},
info: {
headCell: 'text-info',
cellTrigger: 'focus-visible:ring-info'
},
warning: {
headCell: 'text-warning',
cellTrigger: 'focus-visible:ring-warning'
},
error: {
headCell: 'text-error',
cellTrigger: 'focus-visible:ring-error'
},
neutral: {
headCell: 'text-highlighted',
cellTrigger: 'focus-visible:ring-inverted'
}
},
variant: {
solid: '',
outline: '',
soft: '',
subtle: ''
},
size: {
xs: {
heading: 'text-xs',
cell: 'text-xs',
headCell: 'text-[10px]',
cellTrigger: 'size-7',
body: 'space-y-2 pt-2'
},
sm: {
heading: 'text-xs',
headCell: 'text-xs',
cell: 'text-xs',
cellTrigger: 'size-7'
},
md: {
heading: 'text-sm',
headCell: 'text-xs',
cell: 'text-sm',
cellTrigger: 'size-8'
},
lg: {
heading: 'text-md',
headCell: 'text-md',
cellTrigger: 'size-9 text-md'
},
xl: {
heading: 'text-lg',
headCell: 'text-lg',
cellTrigger: 'size-10 text-lg'
}
}
},
compoundVariants: [
{
color: 'primary',
variant: 'solid',
class: {
cellTrigger: 'data-[selected]:bg-primary data-[selected]:text-inverted data-today:not-data-[selected]:text-primary data-[highlighted]:bg-primary/20 hover:not-data-[selected]:bg-primary/20'
}
},
{
color: 'secondary',
variant: 'solid',
class: {
cellTrigger: 'data-[selected]:bg-secondary data-[selected]:text-inverted data-today:not-data-[selected]:text-secondary data-[highlighted]:bg-secondary/20 hover:not-data-[selected]:bg-secondary/20'
}
},
{
color: 'success',
variant: 'solid',
class: {
cellTrigger: 'data-[selected]:bg-success data-[selected]:text-inverted data-today:not-data-[selected]:text-success data-[highlighted]:bg-success/20 hover:not-data-[selected]:bg-success/20'
}
},
{
color: 'info',
variant: 'solid',
class: {
cellTrigger: 'data-[selected]:bg-info data-[selected]:text-inverted data-today:not-data-[selected]:text-info data-[highlighted]:bg-info/20 hover:not-data-[selected]:bg-info/20'
}
},
{
color: 'warning',
variant: 'solid',
class: {
cellTrigger: 'data-[selected]:bg-warning data-[selected]:text-inverted data-today:not-data-[selected]:text-warning data-[highlighted]:bg-warning/20 hover:not-data-[selected]:bg-warning/20'
}
},
{
color: 'error',
variant: 'solid',
class: {
cellTrigger: 'data-[selected]:bg-error data-[selected]:text-inverted data-today:not-data-[selected]:text-error data-[highlighted]:bg-error/20 hover:not-data-[selected]:bg-error/20'
}
},
{
color: 'primary',
variant: 'outline',
class: {
cellTrigger: 'data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-primary/50 data-[selected]:text-primary data-today:not-data-[selected]:text-primary data-[highlighted]:bg-primary/10 hover:not-data-[selected]:bg-primary/10'
}
},
{
color: 'secondary',
variant: 'outline',
class: {
cellTrigger: 'data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-secondary/50 data-[selected]:text-secondary data-today:not-data-[selected]:text-secondary data-[highlighted]:bg-secondary/10 hover:not-data-[selected]:bg-secondary/10'
}
},
{
color: 'success',
variant: 'outline',
class: {
cellTrigger: 'data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-success/50 data-[selected]:text-success data-today:not-data-[selected]:text-success data-[highlighted]:bg-success/10 hover:not-data-[selected]:bg-success/10'
}
},
{
color: 'info',
variant: 'outline',
class: {
cellTrigger: 'data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-info/50 data-[selected]:text-info data-today:not-data-[selected]:text-info data-[highlighted]:bg-info/10 hover:not-data-[selected]:bg-info/10'
}
},
{
color: 'warning',
variant: 'outline',
class: {
cellTrigger: 'data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-warning/50 data-[selected]:text-warning data-today:not-data-[selected]:text-warning data-[highlighted]:bg-warning/10 hover:not-data-[selected]:bg-warning/10'
}
},
{
color: 'error',
variant: 'outline',
class: {
cellTrigger: 'data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-error/50 data-[selected]:text-error data-today:not-data-[selected]:text-error data-[highlighted]:bg-error/10 hover:not-data-[selected]:bg-error/10'
}
},
{
color: 'primary',
variant: 'soft',
class: {
cellTrigger: 'data-[selected]:bg-primary/10 data-[selected]:text-primary data-today:not-data-[selected]:text-primary data-[highlighted]:bg-primary/20 hover:not-data-[selected]:bg-primary/20'
}
},
{
color: 'secondary',
variant: 'soft',
class: {
cellTrigger: 'data-[selected]:bg-secondary/10 data-[selected]:text-secondary data-today:not-data-[selected]:text-secondary data-[highlighted]:bg-secondary/20 hover:not-data-[selected]:bg-secondary/20'
}
},
{
color: 'success',
variant: 'soft',
class: {
cellTrigger: 'data-[selected]:bg-success/10 data-[selected]:text-success data-today:not-data-[selected]:text-success data-[highlighted]:bg-success/20 hover:not-data-[selected]:bg-success/20'
}
},
{
color: 'info',
variant: 'soft',
class: {
cellTrigger: 'data-[selected]:bg-info/10 data-[selected]:text-info data-today:not-data-[selected]:text-info data-[highlighted]:bg-info/20 hover:not-data-[selected]:bg-info/20'
}
},
{
color: 'warning',
variant: 'soft',
class: {
cellTrigger: 'data-[selected]:bg-warning/10 data-[selected]:text-warning data-today:not-data-[selected]:text-warning data-[highlighted]:bg-warning/20 hover:not-data-[selected]:bg-warning/20'
}
},
{
color: 'error',
variant: 'soft',
class: {
cellTrigger: 'data-[selected]:bg-error/10 data-[selected]:text-error data-today:not-data-[selected]:text-error data-[highlighted]:bg-error/20 hover:not-data-[selected]:bg-error/20'
}
},
{
color: 'primary',
variant: 'subtle',
class: {
cellTrigger: 'data-[selected]:bg-primary/10 data-[selected]:text-primary data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-primary/25 data-today:not-data-[selected]:text-primary data-[highlighted]:bg-primary/20 hover:not-data-[selected]:bg-primary/20'
}
},
{
color: 'secondary',
variant: 'subtle',
class: {
cellTrigger: 'data-[selected]:bg-secondary/10 data-[selected]:text-secondary data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-secondary/25 data-today:not-data-[selected]:text-secondary data-[highlighted]:bg-secondary/20 hover:not-data-[selected]:bg-secondary/20'
}
},
{
color: 'success',
variant: 'subtle',
class: {
cellTrigger: 'data-[selected]:bg-success/10 data-[selected]:text-success data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-success/25 data-today:not-data-[selected]:text-success data-[highlighted]:bg-success/20 hover:not-data-[selected]:bg-success/20'
}
},
{
color: 'info',
variant: 'subtle',
class: {
cellTrigger: 'data-[selected]:bg-info/10 data-[selected]:text-info data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-info/25 data-today:not-data-[selected]:text-info data-[highlighted]:bg-info/20 hover:not-data-[selected]:bg-info/20'
}
},
{
color: 'warning',
variant: 'subtle',
class: {
cellTrigger: 'data-[selected]:bg-warning/10 data-[selected]:text-warning data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-warning/25 data-today:not-data-[selected]:text-warning data-[highlighted]:bg-warning/20 hover:not-data-[selected]:bg-warning/20'
}
},
{
color: 'error',
variant: 'subtle',
class: {
cellTrigger: 'data-[selected]:bg-error/10 data-[selected]:text-error data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-error/25 data-today:not-data-[selected]:text-error data-[highlighted]:bg-error/20 hover:not-data-[selected]:bg-error/20'
}
},
{
color: 'neutral',
variant: 'solid',
class: {
cellTrigger: 'data-[selected]:bg-inverted data-[selected]:text-inverted data-today:not-data-[selected]:text-highlighted data-[highlighted]:bg-inverted/20 hover:not-data-[selected]:bg-inverted/10'
}
},
{
color: 'neutral',
variant: 'outline',
class: {
cellTrigger: 'data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-accented data-[selected]:text-default data-[selected]:bg-default data-today:not-data-[selected]:text-highlighted data-[highlighted]:bg-inverted/10 hover:not-data-[selected]:bg-inverted/10'
}
},
{
color: 'neutral',
variant: 'soft',
class: {
cellTrigger: 'data-[selected]:bg-elevated data-[selected]:text-default data-today:not-data-[selected]:text-highlighted data-[highlighted]:bg-inverted/20 hover:not-data-[selected]:bg-inverted/10'
}
},
{
color: 'neutral',
variant: 'subtle',
class: {
cellTrigger: 'data-[selected]:bg-elevated data-[selected]:text-default data-[selected]:ring data-[selected]:ring-inset data-[selected]:ring-accented data-today:not-data-[selected]:text-highlighted data-[highlighted]:bg-inverted/20 hover:not-data-[selected]:bg-inverted/10'
}
}
],
defaultVariants: {
size: 'md',
color: 'primary',
variant: 'solid'
}
}
}
})
```
## Changelog
::component-changelog
::
# Card
## Usage
Use the `header`, `default` and `footer` slots to add content to the Card.
```vue [CardExample.vue]
```
### Variant
Use the `variant` prop to change the variant of the Card.
```vue
```
## API
### Props
```ts
/**
* Props for the Card component
*/
interface CardProps {
/**
* The element or component this component should render as.
*/
as?: any;
variant?: "solid" | "outline" | "soft" | "subtle" | undefined;
ui?: { root?: ClassNameValue; header?: ClassNameValue; body?: ClassNameValue; footer?: ClassNameValue; } | undefined;
}
```
### Slots
```ts
/**
* Slots for the Card component
*/
interface CardSlots {
header(): any;
default(): any;
footer(): any;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
card: {
slots: {
root: 'rounded-lg overflow-hidden',
header: 'p-4 sm:px-6',
body: 'p-4 sm:p-6',
footer: 'p-4 sm:px-6'
},
variants: {
variant: {
solid: {
root: 'bg-inverted text-inverted'
},
outline: {
root: 'bg-default ring ring-default divide-y divide-default'
},
soft: {
root: 'bg-elevated/50 divide-y divide-default'
},
subtle: {
root: 'bg-elevated/50 ring ring-default divide-y divide-default'
}
}
},
defaultVariants: {
variant: 'outline'
}
}
}
})
```
## Changelog
::component-changelog
::
# Carousel
## Usage
Use the Carousel component to display a list of items in a carousel.
```vue [CarouselItemsExample.vue]
```
::note
Use your mouse to drag the carousel horizontally on desktop.
::
### Items
Use the `items` prop as an array and render each item using the default slot:
```vue [CarouselItemsExample.vue]
```
You can also pass an array of objects with the following properties:
- `class?: any`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `ui?: { item?: ClassNameValue }`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
You can control how many items are visible by using the [`basis`](https://tailwindcss.com/docs/flex-basis){rel="nofollow"} / [`width`](https://tailwindcss.com/docs/width){rel="nofollow"} utility classes on the `item`:
```vue [CarouselItemsMultipleExample.vue]
```
### Orientation
Use the `orientation` prop to change the orientation of the Progress. Defaults to `horizontal`.
::note
Use your mouse to drag the carousel vertically on desktop.
::
```vue [CarouselOrientationExample.vue]
```
::caution
You need to specify a `height` on the container in vertical orientation.
::
### Arrows
Use the `arrows` prop to display prev and next buttons.
```vue [CarouselArrowsExample.vue]
```
### Prev / Next
Use the `prev` and `next` props to customize the prev and next buttons with any [Button](https://ui.nuxt.com/docs/components/button) props.
```vue [CarouselPrevNextExample.vue]
```
### Prev / Next Icons
Use the `prev-icon` and `next-icon` props to customize the buttons [Icon](https://ui.nuxt.com/docs/components/icon). Defaults to `i-lucide-arrow-left` / `i-lucide-arrow-right`.
```vue [CarouselPrevNextIconExample.vue]
```
::framework-only
#nuxt
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/nuxt#theme
---
You can customize these icons globally in your `app.config.ts` under `ui.icons.arrowLeft` / `ui.icons.arrowRight` key.
:::
#vue
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/vue#theme
---
You can customize these icons globally in your `vite.config.ts` under `ui.icons.arrowLeft` / `ui.icons.arrowRight` key.
:::
::
### Dots
Use the `dots` prop to display a list of dots to scroll to a specific slide.
```vue [CarouselDotsExample.vue]
```
The number of dots is based on the number of slides displayed in the view:
```vue [CarouselDotsMultipleExample.vue]
```
## Plugins
The Carousel component implements the official [Embla Carousel plugins](https://www.embla-carousel.com/plugins/){rel="nofollow"}.
### Autoplay
This plugin is used to extend Embla Carousel with **autoplay** functionality.
Use the `autoplay` prop as a boolean or an object to configure the [Autoplay plugin](https://www.embla-carousel.com/plugins/autoplay/){rel="nofollow"}.
```vue [CarouselAutoplayExample.vue]
```
::note
In this example, we're using the `loop` prop for an infinite carousel.
::
### Auto Scroll
This plugin is used to extend Embla Carousel with **auto scroll** functionality.
Use the `auto-scroll` prop as a boolean or an object to configure the [Auto Scroll plugin](https://www.embla-carousel.com/plugins/auto-scroll/){rel="nofollow"}.
```vue [CarouselAutoScrollExample.vue]
```
::note
In this example, we're using the `loop` prop for an infinite carousel.
::
### Auto Height
This plugin is used to extend Embla Carousel with **auto height** functionality. It changes the height of the carousel container to fit the height of the highest slide in view.
Use the `auto-height` prop as a boolean or an object to configure the [Auto Height plugin](https://www.embla-carousel.com/plugins/auto-height/){rel="nofollow"}.
```vue [CarouselAutoHeightExample.vue]
```
::note
In this example, we add the `transition-[height]` class on the container to animate the height change.
::
### Class Names
Class Names is a **class name toggle** utility plugin for Embla Carousel that enables you to automate the toggling of class names on your carousel.
Use the `class-names` prop as a boolean or an object to configure the [Class Names plugin](https://www.embla-carousel.com/plugins/class-names/){rel="nofollow"}.
```vue [CarouselClassNamesExample.vue]
```
::note
In this example, we add the `transition-opacity [&:not(.is-snapped)]:opacity-10` classes on the `item` to animate the opacity change.
::
### Fade
This plugin is used to replace the Embla Carousel scroll functionality with **fade transitions**.
Use the `fade` prop as a boolean or an object to configure the [Fade plugin](https://www.embla-carousel.com/plugins/fade/){rel="nofollow"}.
```vue [CarouselFadeExample.vue]
```
### Wheel Gestures
This plugin is used to extend Embla Carousel with the ability to **use the mouse/trackpad wheel** to navigate the carousel.
Use the `wheel-gestures` prop as a boolean or an object to configure the [Wheel Gestures plugin](https://www.embla-carousel.com/plugins/wheel-gestures/){rel="nofollow"}.
::note
Use your mouse wheel to scroll the carousel.
::
```vue [CarouselWheelGesturesExample.vue]
```
## Examples
### With thumbnails
You can use the [`emblaApi`](https://ui.nuxt.com/#expose) function [scrollTo](https://www.embla-carousel.com/api/methods/#scrollto){rel="nofollow"} to display thumbnails under the carousel that allows you to navigate to a specific slide.
```vue [CarouselThumbnailsExample.vue]
```
## API
### Props
```ts
/**
* Props for the Carousel component
*/
interface CarouselProps {
/**
* The element or component this component should render as.
*/
as?: any;
/**
* Configure the prev button when arrows are enabled.
*/
prev?: ButtonProps | undefined;
/**
* The icon displayed in the prev button.
*/
prevIcon?: string | object | undefined;
/**
* Configure the next button when arrows are enabled.
*/
next?: ButtonProps | undefined;
/**
* The icon displayed in the next button.
*/
nextIcon?: string | object | undefined;
/**
* Display prev and next buttons to scroll the carousel.
* @default "false"
*/
arrows?: boolean | undefined;
/**
* Display dots to scroll to a specific slide.
* @default "false"
*/
dots?: boolean | undefined;
/**
* The orientation of the carousel.
* @default "\"horizontal\""
*/
orientation?: "vertical" | "horizontal" | undefined;
items?: CarouselItem[] | undefined;
/**
* Enable Autoplay plugin
* @default "false"
*/
autoplay?: boolean | Partial> | undefined;
/**
* Enable Auto Scroll plugin
* @default "false"
*/
autoScroll?: boolean | Partial> | undefined;
/**
* Enable Auto Height plugin
* @default "false"
*/
autoHeight?: boolean | Partial, "breakpoints">; }; }>> | undefined;
/**
* Enable Class Names plugin
* @default "false"
*/
classNames?: boolean | Partial> | undefined;
/**
* Enable Fade plugin
* @default "false"
*/
fade?: boolean | Partial, "breakpoints">; }; }>> | undefined;
/**
* Enable Wheel Gestures plugin
* @default "false"
*/
wheelGestures?: boolean | WheelGesturesPluginOptions | undefined;
ui?: { root?: ClassNameValue; viewport?: ClassNameValue; container?: ClassNameValue; item?: ClassNameValue; controls?: ClassNameValue; arrows?: ClassNameValue; prev?: ClassNameValue; next?: ClassNameValue; dots?: ClassNameValue; dot?: ClassNameValue; } | undefined;
/**
* @default "\"center\""
*/
align?: AlignmentOptionType | undefined;
/**
* @default "\"trimSnaps\""
*/
containScroll?: ScrollContainOptionType | undefined;
/**
* @default "1"
*/
slidesToScroll?: SlidesToScrollOptionType | undefined;
/**
* @default "false"
*/
dragFree?: boolean | undefined;
/**
* @default "10"
*/
dragThreshold?: number | undefined;
/**
* @default "0"
*/
inViewThreshold?: number | number[] | undefined;
/**
* @default "false"
*/
loop?: boolean | undefined;
/**
* @default "false"
*/
skipSnaps?: boolean | undefined;
/**
* @default "25"
*/
duration?: number | undefined;
/**
* @default "0"
*/
startIndex?: number | undefined;
/**
* @default "true"
*/
watchDrag?: DragHandlerOptionType | undefined;
/**
* @default "true"
*/
watchResize?: ResizeHandlerOptionType | undefined;
/**
* @default "true"
*/
watchSlides?: SlidesHandlerOptionType | undefined;
/**
* @default "true"
*/
watchFocus?: FocusHandlerOptionType | undefined;
/**
* @default "true"
*/
active?: boolean | undefined;
/**
* @default "{}"
*/
breakpoints?: { [key: string]: Omit | null; containScroll: ScrollContainOptionType; direction: AxisDirectionOptionType; slidesToScroll: SlidesToScrollOptionType; dragFree: boolean; dragThreshold: number; inViewThreshold: number | number[] | undefined; loop: boolean; skipSnaps: boolean; duration: number; startIndex: number; watchDrag: DragHandlerOptionType; watchResize: ResizeHandlerOptionType; watchSlides: SlidesHandlerOptionType; watchFocus: FocusHandlerOptionType; }>>, "breakpoints">; } | undefined;
}
```
### Slots
```ts
/**
* Slots for the Carousel component
*/
interface CarouselSlots {
default(): any;
}
```
### Emits
```ts
/**
* Emitted events for the Carousel component
*/
interface CarouselEmits {
select: (payload: [selectedIndex: number]) => void;
}
```
### Expose
You can access the typed component instance using [`useTemplateRef`](https://vuejs.org/api/composition-api-helpers.html#usetemplateref){rel="nofollow"}.
```vue
```
This will give you access to the following:
| Name | Type |
| ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `emblaRef`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | `Ref`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} |
| `emblaApi`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"} | [`Ref`{.language-ts-type.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}](https://www.embla-carousel.com/api/methods/#typescript){rel="nofollow"} |
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
carousel: {
slots: {
root: 'relative focus:outline-none',
viewport: 'overflow-hidden',
container: 'flex items-start',
item: 'min-w-0 shrink-0 basis-full',
controls: '',
arrows: '',
prev: 'absolute rounded-full',
next: 'absolute rounded-full',
dots: 'absolute inset-x-0 -bottom-7 flex flex-wrap items-center justify-center gap-3',
dot: [
'cursor-pointer size-3 bg-accented rounded-full',
'transition'
]
},
variants: {
orientation: {
vertical: {
container: 'flex-col -mt-4',
item: 'pt-4',
prev: 'top-4 sm:-top-12 left-1/2 -translate-x-1/2 rotate-90 rtl:-rotate-90',
next: 'bottom-4 sm:-bottom-12 left-1/2 -translate-x-1/2 rotate-90 rtl:-rotate-90'
},
horizontal: {
container: 'flex-row -ms-4',
item: 'ps-4',
prev: 'start-4 sm:-start-12 top-1/2 -translate-y-1/2',
next: 'end-4 sm:-end-12 top-1/2 -translate-y-1/2'
}
},
active: {
true: {
dot: 'data-[state=active]:bg-inverted'
}
}
}
}
}
})
```
## Changelog
::component-changelog
::
# ChangelogVersion
## Usage
The ChangelogVersion component provides a flexible way to display an `` element with customizable content including title, description, image, etc.
::code-preview
:::u-changelog-version
---
authors:
- name: Benjamin Canac
description: "@benjamincanac"
avatar:
src: https://github.com/benjamincanac.png
to: https://x.com/benjamincanac
target: _blank
- name: Sebastien Chopin
description: "@atinux"
avatar:
src: https://github.com/atinux.png
to: https://x.com/atinux
target: _blank
- name: Hugo Richard
description: "@hugorcd__"
avatar:
src: https://github.com/hugorcd.png
to: https://x.com/hugorcd__
target: _blank
ui:
container: max-w-lg
class: w-full
date: 2025-03-12
description: Nuxt UI v3 is out! After 1500+ commits, this major redesign brings
improved accessibility, Tailwind CSS v4 support, and full Vue compatibility.
image: https://nuxt.com/assets/blog/nuxt-ui-v3.png
target: _blank
title: Introducing Nuxt UI v3
to: https://nuxt.com/blog/nuxt-ui-v3
---
:::
::
::tip{to="https://ui.nuxt.com/docs/components/changelog-versions"}
Use the [`ChangelogVersions`](https://ui.nuxt.com/docs/components/changelog-versions) component to display multiple changelog versions in a timeline with an indicator bar on the left.
::
### Title
Use the `title` prop to display the title of the ChangelogVersion.
```vue
```
### Description
Use the `description` prop to display the description of the ChangelogVersion.
```vue
```
### Date
Use the `date` prop to display the date of the ChangelogVersion.
::tip
The date is automatically formatted to the [current locale](https://ui.nuxt.com/docs/getting-started/integrations/i18n/nuxt#locale). You can either pass a `Date` object or a string.
::
```vue
```
### Badge
Use the `badge` prop to display a [Badge](https://ui.nuxt.com/docs/components/badge) on the ChangelogVersion.
```vue
```
You can pass any property from the [Badge](https://ui.nuxt.com/docs/components/badge#props) component to customize it.
```vue
```
### Image
Use the `image` prop to display an image in the BlogPost.
::note
If [`@nuxt/image`](https://image.nuxt.com/get-started/installation){rel="nofollow"} is installed, the `` component will be used instead of the native `img` tag.
::
```vue
```
### Authors
Use the `authors` prop to display a list of [User](https://ui.nuxt.com/docs/components/user) in the ChangelogVersion as an array of objects with the following properties:
- `name?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `description?: string`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `avatar?: Omit`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `chip?: boolean | Omit`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `size?: UserProps['size']`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `orientation?: UserProps['orientation']`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
You can pass any property from the [Link](https://ui.nuxt.com/docs/components/link#props) component such as `to`, `target`, etc.
```vue
```
### Link
You can pass any property from the [``](https://nuxt.com/docs/api/components/nuxt-link){rel="nofollow"} component such as `to`, `target`, `rel`, etc.
```vue
```
### Indicator
Use the `indicator` prop to hide the indicator dot on the left. Defaults to `true`.
```vue
```
::note
When the `indicator` prop is `false`, the date will be displayed over the title.
::
## Examples
### With body slot
You can use the `body` slot to display custom content between the image and the authors with:
- the [MDC](https://github.com/nuxt-modules/mdc?tab=readme-ov-file#mdc){rel="nofollow"} component from `@nuxtjs/mdc` to display some markdown.
- the [ContentRenderer](https://content.nuxt.com/docs/components/content-renderer){rel="nofollow"} component from `@nuxt/content` to render the content of the page or list.
- or use the `:u-changelog-version` component directly in your content with markdown inside the `body` slot as Nuxt UI provides pre-styled prose components.
```vue [ChangelogVersionMarkdownExample.vue]
```
## API
### Props
```ts
/**
* Props for the ChangelogVersion component
*/
interface ChangelogVersionProps {
/**
* The element or component this component should render as.
* @default "\"article\""
*/
as?: any;
title?: string | undefined;
description?: string | undefined;
/**
* The date of the changelog version. Can be a string or a Date object.
*/
date?: string | Date | undefined;
/**
* Display a badge on the changelog version.
* Can be a string or an object.
* `{ color: 'neutral', variant: 'solid' }`{lang="ts-type"}
*/
badge?: string | BadgeProps | undefined;
/**
* The authors of the changelog version.
*/
authors?: UserProps[] | undefined;
/**
* The image of the changelog version. Can be a string or an object.
*/
image?: string | (Partial & { [key: string]: any; }) | undefined;
/**
* Display an indicator dot on the left.
* @default "true"
*/
indicator?: boolean | undefined;
to?: string | RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric | undefined;
target?: "_blank" | "_parent" | "_self" | "_top" | (string & {}) | null | undefined;
onClick?: ((event: MouseEvent) => void | Promise) | undefined;
ui?: { root?: ClassNameValue; container?: ClassNameValue; header?: ClassNameValue; meta?: ClassNameValue; date?: ClassNameValue; badge?: ClassNameValue; title?: ClassNameValue; description?: ClassNameValue; imageWrapper?: ClassNameValue; image?: ClassNameValue; authors?: ClassNameValue; footer?: ClassNameValue; indicator?: ClassNameValue; dot?: ClassNameValue; dotInner?: ClassNameValue; } | undefined;
}
```
### Slots
```ts
/**
* Slots for the ChangelogVersion component
*/
interface ChangelogVersionSlots {
header(): any;
badge(): any;
date(): any;
title(): any;
description(): any;
image(): any;
body(): any;
footer(): any;
authors(): any;
actions(): any;
indicator(): any;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
changelogVersion: {
slots: {
root: 'relative',
container: 'flex flex-col mx-auto max-w-2xl',
header: '',
meta: 'flex items-center gap-3 mb-2',
date: 'text-sm/6 text-toned truncate',
badge: '',
title: 'relative text-xl text-pretty font-semibold text-highlighted',
description: 'text-base text-pretty text-muted mt-1',
imageWrapper: 'relative overflow-hidden rounded-lg aspect-[16/9] mt-5 group/changelog-version-image',
image: 'object-cover object-top w-full h-full',
authors: 'flex flex-wrap gap-x-4 gap-y-1.5',
footer: 'border-t border-default pt-5 flex items-center justify-between',
indicator: 'absolute start-0 top-0 w-32 hidden lg:flex items-center justify-end gap-3 min-w-0',
dot: 'size-4 rounded-full bg-default ring ring-default flex items-center justify-center my-1',
dotInner: 'size-2 rounded-full bg-primary'
},
variants: {
body: {
false: {
footer: 'mt-5'
}
},
badge: {
false: {
meta: 'lg:hidden'
}
},
to: {
true: {
image: 'transform transition-transform duration-200 group-hover/changelog-version-image:scale-105'
}
},
hidden: {
true: {
date: 'lg:hidden'
}
}
}
}
}
})
```
## Changelog
::component-changelog
::
# ChangelogVersions
## Usage
The ChangelogVersions component provides a flexible layout to display a list of [ChangelogVersion](https://ui.nuxt.com/docs/components/changelog-version) components using either the default slot or the `versions` prop.
```vue {2,8}
```
### Versions
Use the `versions` prop as an array of objects with the properties of the [ChangelogVersion](https://ui.nuxt.com/docs/components/changelog-version#props) component.
```vue
```
### Indicator
Use the `indicator` prop to hide the indicator bar on the left. Defaults to `true`.
```vue
```
### Indicator Motion
Use the `indicator-motion` prop to customize or hide the motion effect on the indicator bar. Defaults to `true` with `{ damping: 30, restDelta: 0.001 }` [spring transition options](https://motion.dev/docs/vue-transitions#spring){rel="nofollow"}.
```vue
```
## Examples
::note
While these examples use [Nuxt Content](https://content.nuxt.com){rel="nofollow"}, the components can be integrated with any content management system.
::
### Within a page
Use the ChangelogVersions component in a page to create a changelog page:
```vue [pages/changelog.vue] {10-17}
```
::note
In this example, the `versions` are fetched using `queryCollection` from the `@nuxt/content` module.
::
::tip
The `to` prop is overridden here since `@nuxt/content` uses the `path` property.
::
### With sticky indicator
You can use the `ui` prop and the different slots to make the indicators sticky:
```vue [ChangelogVersionsStickyExample.vue]
{{ new Date(version.date).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }) }}
```
## API
### Props
```ts
/**
* Props for the ChangelogVersions component
*/
interface ChangelogVersionsProps {
/**
* The element or component this component should render as.
*/
as?: any;
versions?: ChangelogVersionProps[] | undefined;
/**
* Display an indicator bar on the left.
* @default "true"
*/
indicator?: boolean | undefined;
/**
* Enable scrolling motion effect on the indicator bar.
* `{ damping: 30, restDelta: 0.001 }`{lang="ts-type"}
* @default "true"
*/
indicatorMotion?: boolean | SpringOptions | undefined;
ui?: { root?: ClassNameValue; container?: ClassNameValue; indicator?: ClassNameValue; beam?: ClassNameValue; } | undefined;
}
```
### Slots
```ts
/**
* Slots for the ChangelogVersions component
*/
interface ChangelogVersionsSlots {
header(): any;
badge(): any;
date(): any;
title(): any;
description(): any;
image(): any;
body(): any;
footer(): any;
authors(): any;
actions(): any;
indicator(): any;
default(): any;
}
```
::tip
You can use all the slots of the [`ChangelogVersion`](https://ui.nuxt.com/docs/components/changelog-version#slots) component inside ChangelogVersions, they are automatically forwarded allowing you to customize individual versions when using the `versions` prop.
```vue {3-5}
```
::
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
changelogVersions: {
slots: {
root: 'relative',
container: 'flex flex-col gap-y-8 sm:gap-y-12 lg:gap-y-16',
indicator: 'absolute hidden lg:block overflow-hidden inset-y-3 start-32 h-full w-px bg-border -ms-[8.5px]',
beam: 'absolute start-0 top-0 w-full bg-primary will-change-[height]'
}
}
}
})
```
## Changelog
::component-changelog
::
# ChatMessage
## Usage
The ChatMessage component renders an `` element for a `user` or `assistant` chat message.
::code-preview
:::u-chat-message
---
avatar:
src: https://github.com/benjamincanac.png
parts:
- type: text
id: "1"
text: Hello! Tell me more about building AI chatbots with Nuxt UI.
id: "1"
role: user
side: right
variant: soft
---
:::
::
::tip{to="https://ui.nuxt.com/docs/components/chat-messages"}
Use the [`ChatMessages`](https://ui.nuxt.com/docs/components/chat-messages) component to display a list of chat messages.
::
### Parts
Use the `parts` prop to display the message content using the AI SDK v5 format.
```vue
```
::note
The `parts` prop is the recommended format for AI SDK v5. Each part has a `type` (e.g., 'text') and corresponding content. The ChatMessage component also supports the deprecated `content` prop for backward compatibility.
::
### Side
Use the `side` prop to display the message on the left or right.
```vue
```
::note
When using the [`ChatMessages`](https://ui.nuxt.com/docs/components/chat-messages) component, the `side` prop is set to `left` for `assistant` messages and `right` for `user` messages.
::
### Variant
Use the `variant` prop to change style of the message.
```vue
```
::note
When using the [`ChatMessages`](https://ui.nuxt.com/docs/components/chat-messages) component, the `variant` prop is set to `naked` for `assistant` messages and `soft` for `user` messages.
::
### Icon
Use the `icon` prop to display an [Icon](https://ui.nuxt.com/docs/components/icon) component next to the message.
```vue
```
### Avatar
Use the `avatar` prop to display an [Avatar](https://ui.nuxt.com/docs/components/avatar) component next to the message.
```vue
```
You can also use the `avatar.icon` prop to display an icon as the avatar.
```vue
```
### Actions
Use the `actions` prop to display actions below the message that will be displayed when hovering over the message.
```vue
```
## API
### Props
```ts
/**
* Props for the ChatMessage component
*/
interface ChatMessageProps {
/**
* A unique identifier for the message.
*/
id: string;
/**
* The role of the message.
*/
role: "system" | "user" | "assistant";
/**
* The parts of the message. Use this for rendering the message in the UI.
*
* System messages should be avoided (set the system prompt on the server instead).
* They can have text parts.
*
* User messages can have text parts and file parts.
*
* Assistant messages can have text, reasoning, tool invocation, and file parts.
*/
parts: UIMessagePart[];
/**
* The element or component this component should render as.
* @default "\"article\""
*/
as?: any;
icon?: string | object | undefined;
avatar?: (AvatarProps & { [key: string]: any; }) | undefined;
variant?: "solid" | "outline" | "soft" | "subtle" | "naked" | undefined;
side?: "left" | "right" | undefined;
/**
* Display a list of actions under the message.
* The `label` will be used in a tooltip.
* `{ size: 'xs', color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
*/
actions?: (Omit & { onClick?: ((e: MouseEvent, message: UIMessage) => void) | undefined; })[] | undefined;
/**
* Render the message in a compact style.
* This is done automatically when used inside a `UChatPalette`{lang="ts-type"}.
*/
compact?: boolean | undefined;
content?: string | undefined;
ui?: { root?: ClassNameValue; container?: ClassNameValue; leading?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; content?: ClassNameValue; actions?: ClassNameValue; } | undefined;
/**
* The metadata of the message.
*/
metadata?: unknown;
}
```
### Slots
```ts
/**
* Slots for the ChatMessage component
*/
interface ChatMessageSlots {
leading(): any;
content(): any;
actions(): any;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
chatMessage: {
slots: {
root: 'group/message relative w-full',
container: 'relative flex items-start',
leading: 'inline-flex items-center justify-center min-h-6',
leadingIcon: 'shrink-0',
leadingAvatar: 'shrink-0',
leadingAvatarSize: '',
content: 'relative text-pretty min-w-0 *:first:mt-0 *:last:mb-0',
actions: [
'opacity-0 group-hover/message:opacity-100 absolute bottom-0 flex items-center',
'transition-opacity'
]
},
variants: {
variant: {
solid: {
content: 'bg-inverted text-inverted'
},
outline: {
content: 'bg-default ring ring-default'
},
soft: {
content: 'bg-elevated/50'
},
subtle: {
content: 'bg-elevated/50 ring ring-default'
},
naked: {
content: ''
}
},
side: {
left: {
container: 'rtl:justify-end'
},
right: {
container: 'ltr:justify-end ms-auto max-w-[75%]'
}
},
leading: {
true: ''
},
actions: {
true: ''
},
compact: {
true: {
root: 'scroll-mt-3',
container: 'gap-1.5 pb-3',
leadingIcon: 'size-5',
leadingAvatarSize: '2xs'
},
false: {
root: 'scroll-mt-4 sm:scroll-mt-6',
container: 'gap-3 pb-8',
leadingIcon: 'size-8',
leadingAvatarSize: 'md'
}
}
},
compoundVariants: [
{
compact: true,
actions: true,
class: {
container: 'pb-8'
}
},
{
leading: true,
compact: false,
side: 'left',
class: {
actions: 'left-11'
}
},
{
leading: true,
compact: true,
side: 'left',
class: {
actions: 'left-6.5'
}
},
{
variant: [
'solid',
'outline',
'soft',
'subtle'
],
compact: false,
class: {
content: 'px-4 py-3 rounded-lg min-h-12',
leading: 'mt-2'
}
},
{
variant: [
'solid',
'outline',
'soft',
'subtle'
],
compact: true,
class: {
content: 'px-2 py-1 rounded-lg min-h-8',
leading: 'mt-1'
}
},
{
variant: 'naked',
side: 'left',
class: {
content: 'w-full'
}
}
],
defaultVariants: {
variant: 'naked'
}
}
}
})
```
## Changelog
::component-changelog
::
# ChatMessages
## Usage
The ChatMessages component displays a list of [ChatMessage](https://ui.nuxt.com/docs/components/chat-message) components using either the default slot or the `messages` prop.
```vue {2,8}
```
::callout{icon="i-lucide-rocket"}
This component is purpose-built for AI chatbots with features like:
- Initial scroll to the bottom upon loading ([`shouldScrollToBottom`](https://ui.nuxt.com/#should-scroll-to-bottom)).
- Continuous scrolling down as new messages arrive ([`shouldAutoScroll`](https://ui.nuxt.com/#should-auto-scroll)).
- An "Auto scroll" button appears when scrolled up, allowing users to jump back to the latest messages ([`autoScroll`](https://ui.nuxt.com/#auto-scroll)).
- A loading indicator displays while the assistant is processing ([`status`](https://ui.nuxt.com/#status)).
- Submitted messages are scrolled to the top of the viewport and the height of the last user message is dynamically adjusted.
::
### Messages
Use the `messages` prop to display a list of chat messages.
```vue
```
### Status
Use the `status` prop to display a visual indicator when the assistant is processing.
```vue
```
::note
Here's the detail of the different statuses from the AI SDK v5 Chat class:
- `submitted`: The message has been sent to the API and we're awaiting the start of the response stream.
- `streaming`: The response is actively streaming in from the API, receiving chunks of data.
- `ready`: The full response has been received and processed; a new user message can be submitted.
- `error`: An error occurred during the API request, preventing successful completion.
::
### User
Use the `user` prop to change the [ChatMessage](https://ui.nuxt.com/docs/components/chat-message) props for `user` messages. Defaults to:
- `side: 'right'`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `variant: 'soft'`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```vue
```
### Assistant
Use the `assistant` prop to change the [ChatMessage](https://ui.nuxt.com/docs/components/chat-message) props for `assistant` messages. Defaults to:
- `side: 'left'`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `variant: 'naked'`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
```vue
```
### Auto Scroll
Use the `auto-scroll` prop to customize or hide the auto scroll button (with `false` value) displayed when scrolling to the top of the chat. Defaults to:
- `color: 'neutral'`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
- `variant: 'outline'`{.shiki.shiki-themes.material-theme-lighter.material-theme.material-theme-palenight lang="ts-type"}
You can pass any property from the [Button](https://ui.nuxt.com/docs/components/button) component to customize it.
```vue
```
### Auto Scroll Icon
Use the `auto-scroll-icon` prop to customize the auto scroll button [Icon](https://ui.nuxt.com/docs/components/icon). Defaults to `i-lucide-arrow-down`.
```vue
```
::framework-only
#nuxt
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/nuxt#theme
---
You can customize this icon globally in your `app.config.ts` under `ui.icons.arrowDown` key.
:::
#vue
:::tip
---
to: https://ui.nuxt.com/docs/getting-started/integrations/icons/vue#theme
---
You can customize this icon globally in your `vite.config.ts` under `ui.icons.arrowDown` key.
:::
::
### Should Auto Scroll
Use the `should-auto-scroll` prop to enable/disable continuous auto scroll while messages are streaming. Defaults to `false`.
```vue
```
### Should Scroll To Bottom
Use the `should-scroll-to-bottom` prop to enable/disable bottom auto scroll when the component is mounted. Defaults to `true`.
```vue
```
## Examples
::note{target="_blank" to="https://ai-sdk.dev/docs/getting-started/nuxt"}
These chat components are designed to be used with the **AI SDK v5** from **Vercel AI SDK**.
::
::callout
---
icon: i-simple-icons-github
target: _blank
to: https://github.com/nuxt-ui-templates/chat
---
Check out the source code of our **AI Chat template** on GitHub for a real-life example.
::
### Within a page
Use the ChatMessages component with the `Chat` class from AI SDK v5 to display a list of chat messages within a page.
Pass the `messages` prop alongside the `status` prop that will be used for the auto scroll and the indicator display.
```vue [pages/[id\\].vue] {2,7-11,24,28}
```
::note
In this example, we use the `MDC` component from [`@nuxtjs/mdc`](https://github.com/nuxt-modules/mdc){rel="nofollow"} to render the content of the message. The `getTextFromMessage` utility extracts the text content from the AI SDK V5 message parts. As Nuxt UI provides pre-styled prose components, your content will be automatically styled.
::
### With indicator slot
You can customize the loading indicator that appears when the status is `submitted`.
```vue [ChatMessagesIndicatorSlotExample.vue]
```
## API
### Props
```ts
/**
* Props for the ChatMessages component
*/
interface ChatMessagesProps {
messages?: UIMessage[] | undefined;
status?: ChatStatus | undefined;
/**
* Whether to automatically scroll to the bottom when a message is streaming.
* @default "false"
*/
shouldAutoScroll?: boolean | undefined;
/**
* Whether to scroll to the bottom on mounted.
* @default "true"
*/
shouldScrollToBottom?: boolean | undefined;
/**
* Display an auto scroll button.
* `{ size: 'md', color: 'neutral', variant: 'outline' }`{lang="ts-type"}
* @default "true"
*/
autoScroll?: boolean | Partial | undefined;
/**
* The icon displayed in the auto scroll button.
*/
autoScrollIcon?: string | object | undefined;
/**
* The `user` messages props.
* `{ side: 'right', variant: 'soft' }`{lang="ts-type"}
*/
user?: Pick | undefined;
/**
* The `assistant` messages props.
* `{ side: 'left', variant: 'naked' }`{lang="ts-type"}
*/
assistant?: Pick | undefined;
/**
* Render the messages in a compact style.
* This is done automatically when used inside a `UChatPalette`{lang="ts-type"}.
*/
compact?: boolean | undefined;
/**
* The spacing offset for the last message in px. Can be useful when the prompt is sticky for example.
* @default "0"
*/
spacingOffset?: number | undefined;
ui?: { root?: ClassNameValue; indicator?: ClassNameValue; viewport?: ClassNameValue; autoScroll?: ClassNameValue; } | undefined;
}
```
### Slots
```ts
/**
* Slots for the ChatMessages component
*/
interface ChatMessagesSlots {
leading(): any;
content(): any;
actions(): any;
default(): any;
indicator(): any;
viewport(): any;
}
```
::tip
You can use all the slots of the [`ChatMessage`](https://ui.nuxt.com/docs/components/chat-message#slots) component inside ChatMessages, they are automatically forwarded allowing you to customize individual messages when using the `messages` prop.
```vue {7-9}
```
::
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
chatMessages: {
slots: {
root: 'w-full flex flex-col gap-1 flex-1 px-2.5 [&>article]:last-of-type:min-h-(--last-message-height)',
indicator: 'h-6 flex items-center gap-1 py-3 *:size-2 *:rounded-full *:bg-elevated [&>*:nth-child(1)]:animate-[bounce_1s_infinite] [&>*:nth-child(2)]:animate-[bounce_1s_0.15s_infinite] [&>*:nth-child(3)]:animate-[bounce_1s_0.3s_infinite]',
viewport: 'absolute inset-x-0 top-[86%] data-[state=open]:animate-[fade-in_200ms_ease-out] data-[state=closed]:animate-[fade-out_200ms_ease-in]',
autoScroll: 'rounded-full absolute right-1/2 translate-x-1/2 bottom-0'
},
variants: {
compact: {
true: '',
false: ''
}
}
}
}
})
```
## Changelog
::component-changelog
::
# ChatPalette
## Usage
The ChatPalette component is a structured layout wrapper that organizes [ChatMessages](https://ui.nuxt.com/docs/components/chat-messages) in a scrollable content area and [ChatPrompt](https://ui.nuxt.com/docs/components/chat-prompt) in a fixed bottom section, creating cohesive chatbot interfaces for modals, slideovers, or drawers.
```vue {2,8}
```
## Examples
::note{target="_blank" to="https://ai-sdk.dev/docs/getting-started/nuxt"}
These chat components are designed to be used with the **AI SDK v5** from **Vercel AI SDK**.
::
### Within a Modal
You can use the ChatPalette component inside a [Modal](https://ui.nuxt.com/docs/components/modal)'s content.
```vue [ChatPaletteModalExample.vue]
```
### Within ContentSearch
You can use the ChatPalette component conditionally inside [ContentSearch](https://ui.nuxt.com/docs/components/content-search)'s content to display a chatbot interface when a user selects an item.
```vue [ChatPaletteContentSearchExample.vue]
```
## API
### Props
```ts
/**
* Props for the ChatPalette component
*/
interface ChatPaletteProps {
/**
* The element or component this component should render as.
*/
as?: any;
ui?: { root?: ClassNameValue; prompt?: ClassNameValue; close?: ClassNameValue; content?: ClassNameValue; } | undefined;
}
```
### Slots
```ts
/**
* Slots for the ChatPalette component
*/
interface ChatPaletteSlots {
default(): any;
prompt(): any;
}
```
## Theme
```ts [app.config.ts]
export default defineAppConfig({
ui: {
chatPalette: {
slots: {
root: 'relative flex-1 flex flex-col min-h-0 min-w-0',
prompt: 'px-0 rounded-t-none border-t border-default',
close: '',
content: 'overflow-y-auto flex-1 flex flex-col py-3'
}
}
}
})
```
## Changelog
::component-changelog
::
# ChatPrompt
## Usage
The ChatPrompt component renders a `