# Introduction :iframe{allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen="true" className="rounded-md" frameBorder="0" height="100%" referrerPolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/_eQxomah-nA?si=pDSzchUBDKb2NQu7" style="aspect-ratio: 16/9;" title="YouTube video player" width="100%"} ## Reka UI We've transitioned from [Headless UI](https://headlessui.com/){rel="nofollow"} to [Reka UI](https://reka-ui.com/){rel="nofollow"} as our core component foundation. This shift brings several key advantages: - **Extensive Component Library**: With 55+ primitives, Reka UI significantly expands our component offerings. - **Active Development**: Reka UI's growing popularity ensures ongoing improvements and updates. - **Enhanced Accessibility**: Built-in accessibility features align with our commitment to inclusive design. - **Vue 3 Optimization**: Seamless integration with Vue 3 and the Composition API. This transition empowers Nuxt UI to become a more comprehensive and flexible UI library, offering developers greater power and customization options. ## Tailwind CSS v4 Nuxt UI integrates the latest Tailwind CSS v4, bringing significant improvements: - **Built for performance**: Full builds in the new engine are up to 5x faster, and incremental builds are over 100x faster — and measured in microseconds. - **Unified toolchain**: Built-in import handling, vendor prefixing, and syntax transforms, with no additional tooling required. - **CSS-first configuration**: A reimagined developer experience where you customize and extend the framework directly in CSS instead of a JavaScript configuration file. - **Designed for the modern web**: Built on native cascade layers, wide-gamut colors, and including first-class support for modern CSS features like container queries, @starting-style, popovers, and more. ::note --- ariaLabel: Tailwind CSS v4 upgrade guide target: _blank to: https://tailwindcss.com/docs/upgrade-guide#changes-from-v3 --- Learn about all the breaking changes in Tailwind CSS v4. :: ## Tailwind Variants We've adopted [Tailwind Variants](https://www.tailwind-variants.org/){rel="nofollow"} to manage our design system, offering: - **Dynamic Styling**: Flexible component variants with a powerful API - **Type Safety**: Full TypeScript support with auto-completion - **Conflict Resolution**: Efficient merging of conflicting styles This integration unifies the styling of components, ensuring consistency and code maintainability. ## TypeScript Integration Nuxt UI offers significantly improved TypeScript integration, providing a superior developer experience: - **Enhanced Auto-completion**: - Full auto-completion for component props based on your theme - Intelligent suggestions for `app.config.ts` theme configuration - **Generic-based Components**: - Built using [Vue 3 Generics](https://vuejs.org/api/sfc-script-setup.html#generics){rel="nofollow"} - Improved type inference for slots and events - **Type-safe Theming**: - Leveraging Tailwind Variants for type-safe styling options - Customizable types for extended theme configurations ::note --- ariaLabel: Accordion component with custom slot to: https://ui.nuxt.com/components/accordion#with-custom-slot --- Check out an example of the Accordion component with auto-completion for props and slots. :: ## Vue compatibility You can now use Nuxt UI in any Vue project without Nuxt by adding the Vite and Vue plugins to your configuration. This provides: - **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/getting-started/installation/vue --- Learn how to install and configure Nuxt UI in a Vue project in the **Vue installation guide**. :: ## Nuxt DevTools Integration You can play with Nuxt UI components as well as your app components directly from Nuxt Devtools with the [compodium](https://github.com/romhml/compodium){rel="nofollow"} module, providing a powerful development experience: - **Component Inspector**: Inspect and analyze Nuxt UI components in real-time - **Live Preview**: Modify component props and see changes instantly - **Code Generation**: Get the corresponding code for your component configurations ::note Install the module to your Nuxt application with one command: ```bash [Terminal] npx nuxi module add compodium ``` :: :video{controls className="w-full,h-auto,rounded" controls="true" poster="https://res.cloudinary.com/nuxt/video/upload/so_0/v1740751953/nuxt-ui/nuxt-compodium_y2bvqw.jpg"} ## Migration We want to be transparent: migrating from Nuxt UI v2 to v3 will require significant effort. While we've maintained core concepts and components, Nuxt UI v3 has been rebuilt from the ground up, resulting in a new library with enhanced capabilities. Key points to consider: - Read our [migration guide](https://ui.nuxt.com/getting-started/migration) to upgrade your project from v2 to v3. - Review the new documentation and components carefully before attempting to upgrade. - If you encounter any issues, please report them on our [GitHub repository](https://github.com/nuxt/ui/issues){rel="nofollow"}. ## FAQ ::accordion :::accordion-item{label="Is Nuxt UI compatible with standalone Vue projects?"} Nuxt UI is now compatible with Vue! You can follow the [installation guide](https://ui.nuxt.com/getting-started/installation/vue) to get started. ::: :::accordion-item{label="What about Nuxt UI Pro?"} We've also rebuilt Nuxt UI Pro from scratch as v3 to match Nuxt UI version. The license you bought or will buy is valid for both Nuxt UI Pro v1 and v3, this is a **free update**. You can follow the [installation guide](https://ui.nuxt.com/getting-started/installation/pro/nuxt) to get started. ::: :::accordion-item --- label: Will Nuxt UI work with other CSS frameworks like UnoCSS? --- Nuxt UI is currently designed to work exclusively with Tailwind CSS. While there's interest in UnoCSS support, implementing it would require significant changes to the theme structure due to differences in class naming conventions. As a result, we don't have plans to add UnoCSS support. ::: :::accordion-item{label="How does Nuxt UI handle accessibility?"} Nuxt UI enhances accessibility through Reka UI integration. This provides automatic ARIA attributes, keyboard navigation support, intelligent focus management, and screen reader announcements. While offering a strong foundation, proper implementation and testing in your specific use case remains crucial for full accessibility compliance. For more detailed information, refer to [Reka UI's accessibility documentation](https://reka-ui.com/docs/overview/accessibility){rel="nofollow"}. ::: :::accordion-item{label="What is the testing approach for Nuxt UI?"} Nuxt UI ensures reliability with 1000+ Vitest tests, covering core functionality and accessibility. This robust testing suite supports the library's stability and serves as a reference for developers. ::: :: --- We're excited about the possibilities Nuxt UI v3 brings to your projects. Explore our documentation to learn more about new features, components, and best practices for building powerful, accessible user interfaces. # Installation ::callout --- className: - hidden icon: i-logos-vue to: https://ui.nuxt.com/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`{className="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'] }) ``` #### 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/components/app"} The `App` component provides global configurations and is required for **Toast**, **Tooltip** components to work as well as **Programmatic Overlays**. ::: :: ### Use our Nuxt Starter Start your project using the [nuxt/starter#ui](https://github.com/nuxt/starter/tree/ui){rel="nofollow"} template with Nuxt UI pre-configured. Create a new project locally by running the following command: ```bash [Terminal] npx nuxi init -t ui ``` ::note The `` argument is the name of the directory where the project will be created, replace it with your project name. :: Once the installation is complete, navigate into your project and start the development server: ```bash [Terminal] cd npm run dev ``` ## 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`{className="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: { prefix: 'Nuxt' } }) ``` ### `fonts` Use the `fonts` option to enable or disable the [`@nuxt/fonts`](https://github.com/nuxt/fonts){rel="nofollow"} module. - Default: `true`{className="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: { 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`{className="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: { 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']`{className="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/getting-started/theme#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`{className="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: { transitions: false } } }) ``` ::note This option adds the `transition-colors` class on components with hover or active states. :: ## 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 `v3` branch. Use them by replacing your package version with the specific commit hash or PR number. ```diff [package.json] { "dependencies": { - "@nuxt/ui": "^3.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 --- className: - hidden icon: i-logos-nuxt-icon to: https://ui.nuxt.com/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`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} ```ts [vite.config.ts] {3,8} import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import ui from '@nuxt/ui/vite' export default defineConfig({ plugins: [ vue(), ui() ] }) ``` :::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 ``` ::: #### Use the Nuxt UI Vue plugin in your `main.ts` ```ts [main.ts] {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') ``` #### Import Tailwind CSS and Nuxt UI in your CSS ```css [assets/main.css] @import "tailwindcss"; @import "@nuxt/ui"; ``` :::tip Import the CSS file in your `main.ts`. ```ts [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') ``` ::: :::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/components/app"} The `App` component provides global configurations and is required for **Toast**, **Tooltip** components to work as well as **Programmatic Overlays**. ::: :: ### Use our Vue starter Start your project using the [nuxtlabs/nuxt-ui-vue-starter](https://github.com/nuxtlabs/nuxt-ui-vue-starter){rel="nofollow"} template with Nuxt UI pre-configured. Create a new project locally by running the following command: ```bash [Terminal] npx nuxi init -t github:nuxtlabs/nuxt-ui-vue-starter ``` ::note The `` argument is the name of the directory where the project will be created, replace it with your project name. :: Once the installation is complete, navigate into your project and start the development server: ```bash [Terminal] cd npm run dev ``` ## 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`{className="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({ 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`{className="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({ 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']`{className="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/getting-started/theme#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`{className="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: { transitions: false } }) ] }) ``` ::note This option adds the `transition-colors` class on components with hover or active states. :: ## 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 `v3` branch. Use them by replacing your package version with the specific commit hash or PR number. ```diff [package.json] { "dependencies": { - "@nuxt/ui": "^3.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 --- className: - hidden icon: i-logos-vue to: https://ui.nuxt.com/getting-started/installation/pro/vue --- Looking for the **Vue** version? :: Nuxt UI Pro is a collection of Vue components, composables and utils built on top of Nuxt UI, oriented on structure and layout and designed to be used as building blocks for your app. ::note{to="https://ui.nuxt.com/getting-started/license"} While Nuxt UI is free and open source, Nuxt UI Pro is a premium product that helps sustain Nuxt OSS development, check out the **License** section to learn more. :: ## Setup ### Add to a Nuxt project ::steps{level="4"} #### Install the Nuxt UI Pro package :::code-group{sync="pm"} ```bash [pnpm] pnpm add @nuxt/ui-pro ``` ```bash [yarn] yarn add @nuxt/ui-pro ``` ```bash [npm] npm install @nuxt/ui-pro ``` ```bash [bun] bun add @nuxt/ui-pro ``` ::: :::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 Pro module in your `nuxt.config.ts`{className="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-pro'] }) ``` :::note The `@nuxt/ui-pro` module automatically includes the `@nuxt/ui` module, so you don't need to install it separately. ::: #### Import Tailwind CSS and Nuxt UI Pro in your CSS :::code-group ```css [assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; ``` ```ts [nuxt.config.ts] {3} export default defineNuxtConfig({ modules: ['@nuxt/ui-pro'], css: ['~/assets/css/main.css'] }) ``` ::: :::note The `@nuxt/ui-pro` CSS file includes the `@nuxt/ui` CSS file, so you don't need to import it separately. ::: :::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/components/app"} The `App` component provides global configurations and is required for **Toast** and **Tooltip** components to work. ::: :: ### Upgrade from Nuxt UI ::steps{level="4"} #### Replace `@nuxt/ui`{className="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="bash"} package with `@nuxt/ui-pro`{className="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="bash"} ```diff [package.json] { "dependencies": { - "@nuxt/ui": "^3.0.0", + "@nuxt/ui-pro": "^3.0.0", } } ``` #### Replace `@nuxt/ui`{className="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="bash"} module with `@nuxt/ui-pro`{className="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="bash"} ```diff [nuxt.config.ts] export default defineNuxtConfig({ - modules: ['@nuxt/ui'], + modules: ['@nuxt/ui-pro'], css: ['~/assets/css/main.css'] }) ``` #### Replace `@nuxt/ui`{className="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="bash"} CSS import with `@nuxt/ui-pro`{className="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="bash"} ```diff [assets/css/main.css] @import "tailwindcss"; - @import "@nuxt/ui"; + @import "@nuxt/ui-pro"; ``` :: ### Use an official template You can get started with our [minimal starter](https://github.com/nuxt-ui-pro/starter){rel="nofollow"} or one of our official templates: ::card-group :::card --- color: neutral icon: i-simple-icons-github target: _blank title: Landing to: https://github.com/nuxt-ui-pro/landing --- A landing page you can use as starting point. ::: :::card --- color: neutral icon: i-simple-icons-github target: _blank title: Docs to: https://github.com/nuxt-ui-pro/docs --- A documentation with Nuxt Content. ::: :::card --- color: neutral icon: i-simple-icons-github target: _blank title: SaaS to: https://github.com/nuxt-ui-pro/saas --- A template with landing, pricing, docs and blog. ::: :::card --- color: neutral icon: i-simple-icons-github target: _blank title: Dashboard to: https://github.com/nuxt-ui-pro/dashboard variant: subtle --- A dashboard with multi-column layout. ::: :: You can use the `Use this template` button on GitHub to create a new repository or use the CLI: ::code-group ```bash [Starter] npx nuxi init -t github:nuxt-ui-pro/starter my-starter ``` ```bash [Landing] npx nuxi init -t github:nuxt-ui-pro/landing my-landing ``` ```bash [Docs] npx nuxi init -t github:nuxt-ui-pro/docs my-docs ``` ```bash [SaaS] npx nuxi init -t github:nuxt-ui-pro/saas my-saas ``` ```bash [Dashboard] npx nuxi init -t github:nuxt-ui-pro/dashboard my-dashboard ``` :: ## Options You can customize Nuxt UI Pro by providing options in your `nuxt.config.ts`. ### `license` Use the `license` option to override the default behavior of reading the license key from the `NUXT_UI_PRO_LICENSE` environment variable. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/ui-pro'], css: ['~/assets/css/main.css'], uiPro: { license: process.env.MY_ENVIRONMENT_VARIABLE } }) ``` ::caution Don't commit your license key to a public repository to avoid exposing it. :: ### `mdc` Use the `mdc` option to force the import of MDC components even if `@nuxtjs/mdc` or `@nuxt/content` is not installed. - Default: `false`{className="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-pro'], css: ['~/assets/css/main.css'], uiPro: { mdc: true } }) ``` ### `content` Use the `content` option to force the import of content components even if `@nuxt/content` is not installed. - Default: `false`{className="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-pro'], css: ['~/assets/css/main.css'], uiPro: { content: true } }) ``` ### `prefix` Use the `prefix` option to change the prefix of the components. - Default: `U`{className="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-pro'], 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`{className="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-pro'], 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`{className="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-pro'], 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']`{className="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-pro'], css: ['~/assets/css/main.css'], ui: { theme: { colors: ['primary', 'error'] } } }) ``` ::tip{to="https://ui.nuxt.com/getting-started/theme#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`{className="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-pro'], css: ['~/assets/css/main.css'], ui: { theme: { transitions: false } } }) ``` ::note This option adds the `transition-colors` class on components with hover or active states. :: ## Continuous Releases Nuxt UI Pro 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 `v3` branch. Use them by replacing your package version with the specific commit hash or PR number. ```diff [package.json] { "dependencies": { - "@nuxt/ui-pro": "^3.0.0", + "@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@fb69f59", } } ``` ::note **pkg.pr.new** will automatically comment on PRs with the installation URL, making it easy to test changes. :: # Installation ::callout --- className: - hidden icon: i-logos-nuxt-icon to: https://ui.nuxt.com/getting-started/installation/pro/nuxt --- Looking for the **Nuxt** version? :: Nuxt UI Pro is a collection of Vue components, composables and utils built on top of Nuxt UI, oriented on structure and layout and designed to be used as building blocks for your app. ::note{to="https://ui.nuxt.com/getting-started/license"} While Nuxt UI is free and open source, Nuxt UI Pro is a premium product that helps sustain Nuxt OSS development, check out the **License** section to learn more. :: ## Setup ### Add to a Vue project ::steps{level="4"} #### Install the Nuxt UI Pro package :::code-group{sync="pm"} ```bash [pnpm] pnpm add @nuxt/ui-pro ``` ```bash [yarn] yarn add @nuxt/ui-pro ``` ```bash [npm] npm install @nuxt/ui-pro ``` ```bash [bun] bun add @nuxt/ui-pro ``` ::: :::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 Pro Vite plugin in your `vite.config.ts`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} ```ts [vite.config.ts] {3,8} import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), uiPro() ] }) ``` :::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 ``` ::: #### Register the Nuxt UI Vue plugin in your `main.ts` ```ts [main.ts] {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') ``` #### Import Tailwind CSS and Nuxt UI Pro in your CSS ```css [assets/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; ``` :::tip Import the CSS file in your `main.ts`. ```ts [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') ``` ::: :::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/components/app"} The `App` component provides global configurations and is required for **Toast** and **Tooltip** components to work. ::: :: ### Upgrade from Nuxt UI ::steps{level="4"} #### Replace `@nuxt/ui`{className="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="bash"} package with `@nuxt/ui-pro`{className="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="bash"} ```diff [package.json] { "dependencies": { - "@nuxt/ui": "^3.0.0", + "@nuxt/ui-pro": "^3.0.0", } } ``` #### Replace `@nuxt/ui`{className="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="bash"} vite plugin with `@nuxt/ui-pro`{className="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="bash"} ```diff [vite.config.ts] import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' - import ui from '@nuxt/ui/vite' + import uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), - ui(), + uiPro() ] }) ``` #### Replace `@nuxt/ui`{className="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="bash"} CSS import with `@nuxt/ui-pro`{className="language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="bash"} ```diff [assets/css/main.css] @import "tailwindcss"; - @import "@nuxt/ui"; + @import "@nuxt/ui-pro"; ``` :: ### Use an official template You can get started with our official Vue templates: ::card-group :::card --- color: neutral icon: i-simple-icons-github target: _blank title: Starter to: https://github.com/nuxt-ui-pro/starter-vue --- A minimal starter with Nuxt UI Pro setup for Vue. ::: :::card --- color: neutral icon: i-simple-icons-github target: _blank title: Dashboard to: https://github.com/nuxt-ui-pro/dashboard-vue variant: subtle --- A Vue dashboard template with multi-column layout. ::: :: You can use the `Use this template` button on GitHub to create a new repository or use the CLI: ::code-group ```bash [Starter] npx giget@latest gh:nuxt-ui-pro/starter-vue my-vue-starter ``` ```bash [Dashboard] npx giget@latest gh:nuxt-ui-pro/dashboard-vue my-vue-dashboard ``` :: Once the directory is created, you can install the dependencies and start the development server using the `npm run dev` command. ## Options You can customize Nuxt UI Pro by providing options in your `vite.config.ts`. ### `prefix` Use the `prefix` option to change the prefix of the components. - Default: `U`{className="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 uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), uiPro({ 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 uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), uiPro({ ui: { colors: { primary: 'green', neutral: 'slate' } } }) ] }) ``` ### `uiPro` Use the `uiPro` option to provide configuration for Nuxt UI Pro. ```ts [vite.config.ts] import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), uiPro({ uiPro: { pageHero: { slots: { title: 'font-light' } } } }) ] }) ``` ### `colorMode` Use the `colorMode` option to enable or disable the color mode integration from `@vueuse/core`. - Default: `true`{className="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 uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), uiPro({ 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']`{className="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 uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), uiPro({ theme: { colors: ['primary', 'error'] } }) ] }) ``` ::tip{to="https://ui.nuxt.com/getting-started/theme#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`{className="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 uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), uiPro({ theme: { transitions: false } }) ] }) ``` ::note This option adds the `transition-colors` class on components with hover or active states. :: ### `license` Use the `license` option to override the default behavior of reading the license key from the `NUXT_UI_PRO_LICENSE` environment variable. ```ts [vite.config.ts] import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), uiPro({ license: process.env.MY_ENVIRONMENT_VARIABLE }) ] }) ``` ::caution Don't commit your license key to a public repository to avoid exposing it. :: ## Continuous Releases Nuxt UI Pro 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 `v3` branch. Use them by replacing your package version with the specific commit hash or PR number. ```diff [package.json] { "dependencies": { - "@nuxt/ui-pro": "^3.0.0", + "@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@fb69f59", } } ``` ::note **pkg.pr.new** will automatically comment on PRs with the installation URL, making it easy to test changes. :: # Migration Nuxt UI v3.0 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: :::module-only #ui ::::div :::::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 ``` ::::: :::: #ui-pro ::::div :::::code-group{sync="pm"} ```bash [pnpm] pnpm add @nuxt/ui-pro ``` ```bash [yarn] yarn add @nuxt/ui-pro ``` ```bash [npm] npm install @nuxt/ui-pro ``` ```bash [bun] bun add @nuxt/ui-pro ``` ::::: :::: ::: 4. Import it in your CSS: :::module-only #ui ::::div ```css [app/assets/css/main.css] {2} @import "tailwindcss"; @import "@nuxt/ui"; ``` :::: #ui-pro ::::div ```css [app/assets/css/main.css] {2} @import "tailwindcss"; @import "@nuxt/ui-pro"; ``` :::: ::: :::module-only #ui ::::div 5. Wrap your app with the [App](https://ui.nuxt.com/components/app) component: :::: #ui-pro ::::div 5. Add the `@nuxt/ui-pro` module in your `nuxt.config.ts` file as it's no longer a layer: ```diff [nuxt.config.ts] export default defineNuxtConfig({ - extends: ['@nuxt/ui-pro'], - modules: ['@nuxt/ui'] + modules: ['@nuxt/ui-pro'] }) ``` 6. Wrap your app with the [App](https://ui.nuxt.com/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/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/getting-started/theme#neutral-palette) 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/getting-started/theme#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/getting-started/theme#components-theme), 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/getting-started/theme#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/getting-started/theme#props) to override each component's slots using their new theme: ```diff ``` ::tip{to="https://ui.nuxt.com/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/components/separator) | | `Dropdown` | [`DropdownMenu`](https://ui.nuxt.com/components/dropdown-menu) | | `FormGroup` | [`FormField`](https://ui.nuxt.com/components/form-field) | | `Range` | [`Slider`](https://ui.nuxt.com/components/slider) | | `Toggle` | [`Switch`](https://ui.nuxt.com/components/switch) | | `Notification` | [`Toast`](https://ui.nuxt.com/components/toast) | | `VerticalNavigation` | [`NavigationMenu`](https://ui.nuxt.com/components/navigation-menu) with `orientation="vertical"` | | `HorizontalNavigation` | [`NavigationMenu`](https://ui.nuxt.com/components/navigation-menu) with `orientation="horizontal"` | ::module-only #ui-pro :::div Here are the Nuxt UI Pro components that have been renamed or removed: | v1 | v3 | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `BlogList` | [`BlogPosts`](https://ui.nuxt.com/components/blog-posts) | | `ColorModeToggle` | [`ColorModeSwitch`](https://ui.nuxt.com/components/color-mode-switch) | | `DashboardCard` | Removed (use [`PageCard`](https://ui.nuxt.com/components/page-card) instead) | | `DashboardLayout` | [`DashboardGroup`](https://ui.nuxt.com/components/dashboard-group) | | `DashboardModal` | Removed (use [`Modal`](https://ui.nuxt.com/components/modal) instead) | | `DashboardNavbarToggle` | [`DashboardSidebarToggle`](https://ui.nuxt.com/components/dashboard-sidebar-toggle) | | `DashboardPage` | Removed | | `DashboardPanelContent` | Removed (use `#body` slot instead) | | `DashboardPanelHandle` | [`DashboardResizeHandle`](https://ui.nuxt.com/components/dashboard-resize-handle) | | `DashboardSection` | Removed (use [`PageCard`](https://ui.nuxt.com/components/page-card) instead) | | `DashboardSidebarLinks` | Removed (use [`NavigationMenu`](https://ui.nuxt.com/components/navigation-menu) instead) | | `DashboardSlideover` | Removed (use [`Slideover`](https://ui.nuxt.com/components/slideover) instead) | | `FooterLinks` | Removed (use [`NavigationMenu`](https://ui.nuxt.com/components/navigation-menu) instead) | | `HeaderLinks` | Removed (use [`NavigationMenu`](https://ui.nuxt.com/components/navigation-menu) instead) | | `LandingCard` | Removed (use [`PageCard`](https://ui.nuxt.com/components/page-card) instead) | | `LandingCTA` | [`PageCTA`](https://ui.nuxt.com/components/page-cta) | | `LandingFAQ` | Removed (use [`PageAccordion`](https://ui.nuxt.com/components/page-accordion) instead) | | `LandingGrid` | Removed (use [`PageGrid`](https://ui.nuxt.com/components/page-grid) instead) | | `LandingHero` | Removed (use [`PageHero`](https://ui.nuxt.com/components/page-hero) instead) | | `LandingLogos` | [`PageLogos`](https://ui.nuxt.com/components/page-logos) | | `LandingSection` | [`PageSection`](https://ui.nuxt.com/components/page-section) | | `LandingTestimonial` | Removed (use [`PageCard`](https://ui.nuxt.com/components/page-card#as-a-testimonial) instead) | | `NavigationAccordion` | [`ContentNavigation`](https://ui.nuxt.com/components/content-navigation) | | `NavigationLinks` | [`ContentNavigation`](https://ui.nuxt.com/components/content-navigation) | | `NavigationTree` | [`ContentNavigation`](https://ui.nuxt.com/components/content-navigation) | | `PageError` | [`Error`](https://ui.nuxt.com/components/error) | | `PricingCard` | [`PricingPlan`](https://ui.nuxt.com/components/pricing-plan) | | `PricingGrid` | [`PricingPlans`](https://ui.nuxt.com/components/pricing-plans) | | `PricingSwitch` | Removed (use [`Switch`](https://ui.nuxt.com/components/switch) or [`Tabs`](https://ui.nuxt.com/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/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 a promise that resolves to the result of the modal whenever the modal is close: ```diff ``` --- ::warning This page is a work in progress, we'll improve it regularly. :: # License ::tip The license you bought or will buy is valid for both Nuxt UI Pro v1 and v3, this is a **free update**. :: ## Plans You can choose between **Solo**, **Startup** and **Organization** plans, they all provide the same features and give you a license key required to build your apps. ::note The only difference is the number of developers that can be invited to the private GitHub repository. :: ::card-group{className="md:grid-cols-3"} :::card --- icon: i-lucide-user title: Solo to: https://nuxt.lemonsqueezy.com/buy/057dacb2-87ba-4dc1-9256-59ee5b3bd394 --- Tailored for indie hackers, freelancers and solo founders. ::: :::card --- icon: i-lucide-users title: Startup to: https://nuxt.lemonsqueezy.com/buy/2e042a33-7e76-4dda-bd68-e353c182e571 --- Best suited for small teams, startups and agencies with up to 5 developers. ::: :::card --- icon: i-lucide-building-2 title: Organization to: https://nuxt.lemonsqueezy.com/buy/2979099c-b7a0-4ba1-90e0-a0d60509b92d --- Ideal for larger teams and organizations with up to 20 developers. ::: :: Once purchased, you will receive an email with a license key to activate. To activate your license, enter your license key and your GitHub username. This will invite you to the private GitHub repository as well as activating your license to build your app in production. ::u-button --- icon: i-lucide-circle-check label: Activate my license to: https://ui.nuxt.com/pro/activate variant: subtle --- :: ## Build ### Local To build your app locally, you can use your license key in your `.env` file: ```sh [.env] NUXT_UI_PRO_LICENSE= ``` ::framework-only #nuxt :::div If you have multiple projects on your machine, you can also add it to your `~/.nuxtrc`: ```sh [~/.nuxtrc] uiPro.license= ``` ::: :: ::warning Never commit your license key if your project is open source, it will be exposed to the public. :: ### Hosting providers When deploying your app on Netlify, Vercel, [NuxtHub](https://hub.nuxt.com){rel="nofollow"} or any other hosting provider, you need to set the `NUXT_UI_PRO_LICENSE` environment variable to your license key. This process is different depending on the provider but here is a quick guide for the most common ones: - [Netlify](https://docs.netlify.com/environment-variables/overview){rel="nofollow"} - [Vercel](https://vercel.com/docs/projects/environment-variables){rel="nofollow"} ::callout --- icon: i-simple-icons-nuxtdotjs target: _blank to: https://hub.nuxt.com --- The `NUXT_UI_PRO_LICENSE` variable is suggested when deploying one of our templates on **NuxtHub**. :: # Theme ## Tailwind CSS Nuxt UI uses Tailwind CSS v4, you can read the official [upgrade guide](https://tailwindcss.com/docs/upgrade-guide#changes-from-v3){rel="nofollow"} to learn about all the breaking changes. ### `@theme` Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your theme with CSS variables inside a [`@theme`](https://tailwindcss.com/docs/functions-and-directives#theme-directive){rel="nofollow"} directive to define your project's custom design tokens, like fonts, colors, and breakpoints: ::module-only #ui :::div ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui"; @theme static { --font-sans: 'Public Sans', sans-serif; --breakpoint-3xl: 1920px; --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; } ``` ::: #ui-pro :::div ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; @theme static { --font-sans: 'Public Sans', sans-serif; --breakpoint-3xl: 1920px; --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; } ``` ::: :: The `@theme` directive tells Tailwind to make new utilities and variants available based on these variables. It's the equivalent of the `theme.extend` key in Tailwind CSS v3 `tailwind.config.ts` file. ::note{target="_blank" to="https://tailwindcss.com/docs/theme"} Learn more about customizing your theme in the theme variables documentation. :: ### `@source` You can use the [`@source` directive](https://tailwindcss.com/docs/functions-and-directives#source-directive){rel="nofollow"} to explicitly specify source files that aren't picked up by Tailwind's automatic content detection: This can be useful when writing Tailwind CSS classes in markdown files with [`@nuxt/content`](https://github.com/nuxt/content){rel="nofollow"} for example: ::module-only #ui :::div ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui"; @source "../../../content"; /* Use this if you're not using compatibilityVersion: 4: https://nuxt.com/docs/getting-started/upgrade#opting-in-to-nuxt-4 */ @source "../../content"; ``` ::: #ui-pro :::div ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; @source "../../../content"; /* Use this if you're not using compatibilityVersion: 4: https://nuxt.com/docs/getting-started/upgrade#opting-in-to-nuxt-4 */ @source "../../content"; ``` ::: :: ::note{to="https://tailwindcss.com/docs/detecting-classes-in-source-files"} Learn more about automatic content detection in the detecting classes in source files documentation. :: ## Design system Nuxt UI extends Tailwind CSS's theming capabilities, providing a flexible design system with pre-configured color aliases and CSS variables. This allows for easy customization and quick adaptation of the UI to your brand's aesthetic. ### Colors ::framework-only #nuxt Nuxt UI leverages Nuxt [App Config](https://nuxt.com/docs/guide/directory-structure/app-config#app-config-file){rel="nofollow"} to provide customizable color aliases based on [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference){rel="nofollow"}: #vue Nuxt UI leverages Vite config to provide customizable color aliases based on [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference){rel="nofollow"}: :: | 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. | ::framework-only #nuxt :::div You can configure these color aliases at runtime in your `app.config.ts` file under the `ui.colors` key, allowing for dynamic theme customization without requiring an application rebuild: ```ts [app.config.ts] export default defineAppConfig({ ui: { colors: { primary: 'blue', neutral: 'zinc' } } }) ``` ::: #vue :::div You can configure these color aliases at runtime in your `vite.config.ts` file under the `ui.colors` key: ::::module-only #ui :::::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: { colors: { primary: 'blue', neutral: 'zinc' } } }) ] }) ``` ::::: #ui-pro :::::div ```ts [vite.config.ts] import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), uiPro({ ui: { colors: { primary: 'blue', neutral: 'zinc' } } }) ] }) ``` ::::: :::: ::: :: ::note Try the :prose-icon{className="text-(--ui-primary)" name="i-lucide-swatch-book"} theme picker in the header above to change `primary` and `neutral` colors. :: These colors are used to style the components but also to generate the `color` variants: ```vue ``` ::framework-only #nuxt :::tip You can add you own dynamic color aliases in your `app.config.ts`, you just have to make sure to define them in the [`ui.theme.colors`](https://ui.nuxt.com/getting-started/installation/nuxt#themecolors) option in your `nuxt.config.ts` file. ```ts [app.config.ts] export default defineAppConfig({ ui: { colors: { tertiary: 'indigo' } } }) ``` ```ts [nuxt.config.ts] export default defineNuxtConfig({ ui: { theme: { colors: ['primary', 'secondary', 'tertiary', 'info', 'success', 'warning', 'error'] } } }) ``` ::: #vue :::tip You can add you own dynamic color aliases in your `vite.config.ts`, you just have to make sure to also define them in the [`theme.colors`](https://ui.nuxt.com/getting-started/installation/vue#themecolors) option of the `ui` plugin. ::::module-only #ui :::::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: { colors: { tertiary: 'indigo' } }, theme: { colors: ['primary', 'secondary', 'tertiary', 'info', 'success', 'warning', 'error'] } }) ] }) ``` ::::: #ui-pro :::::div ```ts [vite.config.ts] import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), uiPro({ ui: { colors: { tertiary: 'indigo' }, }, theme: { colors: ['primary', 'secondary', 'tertiary', 'info', 'success', 'warning', 'error'] } }) ] }) ``` ::::: :::: ::: :: ### Tokens Nuxt UI leverages a robust system of CSS variables as design tokens to ensure consistent and flexible component styling. These tokens form the foundation of the theming system, offering smooth support for both light and dark modes. #### Color Shades Nuxt UI automatically creates a CSS variable for each color alias you define which represent the default shade used in both 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); } ``` :: ::note You can use these variables in your Tailwind CSS classes in two ways: - Using CSS variable syntax: `text-(--ui-primary)` or `bg-(--ui-primary)` - Using color alias: `text-primary` or `bg-primary` Both approaches will automatically adapt to the current color scheme, ensuring consistent styling across light and dark modes. :: ::tip You can change which shade is used for each color on light and dark mode: :::module-only #ui ::::div{className="*:!mb-0,*:!mt-2.5"} ```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); } ``` :::: #ui-pro ::::div{className="*:!mb-0,*:!mt-2.5"} ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; :root { --ui-primary: var(--ui-color-primary-700); } .dark { --ui-primary: var(--ui-color-primary-200); } ``` :::: ::: :: #### Black as Primary Color ::framework-only #nuxt You cannot set `primary: 'black'`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} in your [`app.config.ts`](https://ui.nuxt.com/#config) because this color has no shade, instead you can override the primary color in your `main.css` file to create a black & white theme: #vue You cannot set `primary: 'black'`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} in your [`vite.config.ts`](https://ui.nuxt.com/#config) because this color has no shade, instead you can override the primary color in your `main.css` file to create a black & white theme: :: ::module-only #ui :::div{className="*:!mb-0,*:!mt-2.5"} ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui"; :root { --ui-primary: black; } .dark { --ui-primary: white; } ``` ::: #ui-pro :::div{className="*:!mb-0,*:!mt-2.5"} ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; :root { --ui-primary: black; } .dark { --ui-primary: white; } ``` ::: :: #### Neutral Palette Nuxt UI provides a comprehensive set of design tokens for the `neutral` color palette, ensuring consistent and accessible UI styling across both light and dark modes. These tokens offer fine-grained control over text, background, and border colors: ::code-group ```css [Light] :root { /* Least prominent text */ --ui-text-dimmed: var(--ui-color-neutral-400); /* Slightly muted text */ --ui-text-muted: var(--ui-color-neutral-500); /* Moderately prominent text */ --ui-text-toned: var(--ui-color-neutral-600); /* Default text color */ --ui-text: var(--ui-color-neutral-700); /* Most prominent text */ --ui-text-highlighted: var(--ui-color-neutral-900); /* Main background color */ --ui-bg: var(--color-white); /* Subtle background */ --ui-bg-muted: var(--ui-color-neutral-50); /* Slightly elevated background */ --ui-bg-elevated: var(--ui-color-neutral-100); /* More prominent background */ --ui-bg-accented: var(--ui-color-neutral-200); /* Inverted background color */ --ui-bg-inverted: var(--ui-color-neutral-900); /* Default border color */ --ui-border: var(--ui-color-neutral-200); /* Subtle border */ --ui-border-muted: var(--ui-color-neutral-200); /* More prominent border */ --ui-border-accented: var(--ui-color-neutral-300); /* Inverted border color */ --ui-border-inverted: var(--ui-color-neutral-900); } ``` ```css [Dark] .dark { /* Least prominent text */ --ui-text-dimmed: var(--ui-color-neutral-500); /* Slightly muted text */ --ui-text-muted: var(--ui-color-neutral-400); /* Moderately prominent text */ --ui-text-toned: var(--ui-color-neutral-300); /* Default text color */ --ui-text: var(--ui-color-neutral-200); /* Most prominent text */ --ui-text-highlighted: var(--color-white); /* Main background color */ --ui-bg: var(--ui-color-neutral-900); /* Subtle background */ --ui-bg-muted: var(--ui-color-neutral-800); /* Slightly elevated background */ --ui-bg-elevated: var(--ui-color-neutral-800); /* More prominent background */ --ui-bg-accented: var(--ui-color-neutral-700); /* Inverted background color */ --ui-bg-inverted: var(--color-white); /* Default border color */ --ui-border: var(--ui-color-neutral-800); /* Subtle border */ --ui-border-muted: var(--ui-color-neutral-700); /* More prominent border */ --ui-border-accented: var(--ui-color-neutral-700); /* Inverted border color */ --ui-border-inverted: var(--color-white); } ``` :: ::note Nuxt UI automatically applies a text and background color on the `` element of your app: ```css body { @apply antialiased text-(--ui-text) bg-(--ui-bg); } ``` :: ::tip You can customize these CSS variables to tailor the appearance of your application: :::module-only #ui ::::div{className="*:!mb-0,*:!mt-2.5"} ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui"; :root { --ui-bg: var(--ui-color-neutral-50); --ui-text: var(--ui-color-neutral-900); } .dark { --ui-bg: var(--ui-color-neutral-950); --ui-border: var(--ui-color-neutral-900); } ``` :::: #ui-pro ::::div{className="*:!mb-0,*:!mt-2.5"} ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; :root { --ui-bg: var(--ui-color-neutral-50); --ui-text: var(--ui-color-neutral-900); } .dark { --ui-bg: var(--ui-color-neutral-950); --ui-border: var(--ui-color-neutral-900); } ``` :::: ::: :: #### Border Radius Nuxt UI provides a centralized border radius system through the `--ui-radius` CSS variable, which defaults to `0.25rem`. This system replaces Tailwind CSS's default `rounded-*` utilities while maintaining the familiar class names. The border radius system is built on a scale that multiplies the base radius value: ```css @theme default { --radius-xs: calc(var(--ui-radius) * 0.5); /* 0.125rem */ --radius-sm: var(--ui-radius); /* 0.25rem */ --radius-md: calc(var(--ui-radius) * 1.5); /* 0.375rem */ --radius-lg: calc(var(--ui-radius) * 2); /* 0.5rem */ --radius-xl: calc(var(--ui-radius) * 3); /* 0.75rem */ --radius-2xl: calc(var(--ui-radius) * 4); /* 1rem */ --radius-3xl: calc(var(--ui-radius) * 6); /* 1.5rem */ } ``` You can customize the base radius value in your `main.css` file: ::module-only #ui :::div{className="*:!mb-0,*:!mt-2.5"} ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui"; :root { --ui-radius: 0.5rem; } ``` ::: #ui-pro :::div{className="*:!mb-0,*:!mt-2.5"} ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; :root { --ui-radius: 0.5rem; } ``` ::: :: ::note Try the :prose-icon{className="text-(--ui-primary)" name="i-lucide-swatch-book"} theme picker in the header above to change the base radius value. :: #### Container Nuxt UI provides a global `--ui-container` CSS variable that controls the maximum width of the [Container](https://ui.nuxt.com/components/container) component, which defaults to `var(--container-7xl)`. You can customize this value in your `main.css` file to adjust container widths consistently throughout your application: ::module-only #ui :::div{className="*:!mb-0,*:!mt-2.5"} ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui"; @theme { --container-8xl: 90rem; } :root { --ui-container: var(--container-8xl); } ``` ::: #ui-pro :::div{className="*:!mb-0,*:!mt-2.5"} ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; @theme { --container-8xl: 90rem; } :root { --ui-container: var(--container-8xl); } ``` ::: :: ## Components theme 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. Let's explore the key features of this API: ### Slots Components in Nuxt UI 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/components/card) component as an example: ::code-group ```ts [src/theme/card.ts] export default { slots: { root: 'bg-(--ui-bg) ring ring-(--ui-border) divide-y divide-(--ui-border) 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/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 Nuxt UI components use `variants` to change the `slots` styles based on props. Here's an example of the [Avatar](https://ui.nuxt.com/components/avatar) component: ```ts [src/theme/avatar.ts] export default { slots: { root: 'inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle bg-(--ui-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 ``` The `defaultVariants` property specifies the default values for each variant. It determines how a component looks and behaves when no prop is provided. ::framework-only #nuxt :::tip These default values can be customized in your [`app.config.ts`](https://ui.nuxt.com/#config) to adjust the standard appearance of components throughout your application. ::: #vue :::tip These default values can be customized in your [`vite.config.ts`](https://ui.nuxt.com/#config) to adjust the standard appearance of components throughout your application. ::: :: ## 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 [`v3/src/theme`](https://github.com/nuxt/ui/tree/v3/src/theme){rel="nofollow"}. :: ### Config ::framework-only #nuxt :::div You can override the theme of components globally inside your `app.config.ts` by using the exact same structure as the theme object. Let's say you want to change the font weight of all your buttons, you can do it like this: ```ts [app.config.ts] export default defineAppConfig({ ui: { button: { slots: { base: 'font-bold' } } } }) ``` ::: #vue :::div You can override the theme of components globally inside your `vite.config.ts` by using the exact same structure as the theme object. Let's say you want to change the font weight of all your buttons, you can do it like this: ::::module-only #ui :::::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' } } } }) ] }) ``` ::::: #ui-pro :::::div ```ts [vite.config.ts] import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), uiPro({ ui: { button: { slots: { base: 'font-bold' } } } }) ] }) ``` ::::: :::: ::: :: ::note In this example, the `font-bold` class will override the default `font-medium` class on all buttons. :: ### Props #### `ui` prop You can also override a component's **slots** using the `ui` prop. This has priority over the global configuration and `variants` resolution. ```vue ``` ::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 has priority over the global configuration and `variants` resolution. ```vue ``` ::note In this example, the `font-bold` class will override the default `font-medium` class on this button. :: # Icons ::callout --- className: - hidden icon: i-logos-vue to: https://ui.nuxt.com/getting-started/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/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/components/button) for example: ```vue ``` ## 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 it's 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, `./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: './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 --- className: - hidden icon: i-logos-nuxt-icon to: https://ui.nuxt.com/getting-started/icons/nuxt --- Looking for the **Nuxt** version? :: ## Usage ### Icon Component You can use the [Icon](https://ui.nuxt.com/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/components/button) for example: ```vue ``` ## 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. 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. ::module-only #ui :::div ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui"; @theme { --font-sans: 'Public Sans', sans-serif; } ``` ::: #ui-pro :::div ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; @theme { --font-sans: 'Public Sans', sans-serif; } ``` ::: :: 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 --- className: - hidden icon: i-logos-vue to: https://ui.nuxt.com/getting-started/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. You can simply use the `useColorMode` composable to switch between light and dark modes: ```vue [ColorModeButton.vue] ``` 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 --- className: - hidden icon: i-logos-nuxt-icon to: https://ui.nuxt.com/getting-started/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. You can simply use it to switch between light and dark modes: ```vue [ColorModeButton.vue] ``` You can disable this plugin with the `colorMode` option in your `vite.config.ts`: ::module-only #ui :::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({ colorMode: false }) ] }) ``` ::: #ui-pro :::div ```ts [vite.config.ts] import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import uiPro from '@nuxt/ui-pro/vite' export default defineConfig({ plugins: [ vue(), uiPro({ colorMode: false }) ] }) ``` ::: :: # Internationalization (i18n) ::callout --- className: - hidden icon: i-logos-vue to: https://ui.nuxt.com/getting-started/i18n/vue --- Looking for the **Vue** version? :: ## Usage ::note{to="https://ui.nuxt.com/components/app"} Nuxt UI provides an **App** component that wraps your app to provide global configurations. :: ### Locale ::module-only #ui :::div Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`: ```vue [app.vue] ``` ::: #ui-pro :::div Use the `locale` prop with the locale you want to use from `@nuxt/ui-pro/locale`: ```vue [app.vue] ``` ::: :: ### Custom locale You also have the option to add your own locale using `defineLocale`: ::module-only #ui :::div ```vue [app.vue] ``` ::: #ui-pro :::div ```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) :: ### 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`{className="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', '@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` :::module-only #ui ::::div ```vue [app.vue] ``` :::: #ui-pro ::::div ```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: ::module-only #ui :::div ```vue [app.vue] ``` ::: #ui-pro :::div ```vue [app.vue] ``` ::: :: ## Supported languages ::supported-languages :: # Internationalization (i18n) ::callout --- className: - hidden icon: i-logos-nuxt-icon to: https://ui.nuxt.com/getting-started/i18n/nuxt --- Looking for the **Nuxt** version? :: ## Usage ::note{to="https://ui.nuxt.com/components/app"} Nuxt UI provides an **App** component that wraps your app to provide global configurations. :: ### Locale ::module-only #ui :::div Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`: ```vue [App.vue] ``` ::: #ui-pro :::div Use the `locale` prop with the locale you want to use from `@nuxt/ui-pro/locale`: ```vue [App.vue] ``` ::: :: ### Custom locale You also have the option to add your locale using `defineLocale`: ::module-only #ui :::div ```vue [App.vue] ``` ::: #ui-pro :::div ```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) :: ### 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@10 ``` ```bash [yarn] yarn add vue-i18n@10 ``` ```bash [npm] npm install vue-i18n@10 ``` ```bash [bun] bun add vue-i18n@10 ``` ::: #### 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` :::module-only #ui ::::div ```vue [App.vue] ``` :::: #ui-pro ::::div ```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: ::module-only #ui :::div ```vue [App.vue] ``` ::: #ui-pro :::div ```vue [App.vue] ``` ::: :: ## Supported languages ::supported-languages :: # Content ::warning Nuxt UI Pro v3 is **only compatible with Nuxt Content v3**. If you're using Nuxt Content v2, you must use Nuxt UI Pro v1. :: ## 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-pro', '@nuxt/content' ], css: ['~/assets/css/main.css'] }) ``` ::caution You need to register `@nuxt/content` after `@nuxt/ui-pro` in the `modules` array, otherwise the prose components will not be available. :: ::note{to="https://ui.nuxt.com/getting-started/theme#source"} If your content includes Tailwind CSS classes, make sure to use the `@source` directive in your CSS file. :: ## 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/components/content-search), replacing the need for Algolia DocSearch - a navigation tree with the [ContentNavigation](https://ui.nuxt.com/components/content-navigation) component - a sticky Table of Contents with the [ContentToc](https://ui.nuxt.com/components/content-toc) component - a prev / next navigation with the [ContentSurround](https://ui.nuxt.com/components/content-surround) component ## Typography To make the most out of `@nuxt/content`, we've revamped our typography system in v3. Instead of using the `@tailwindcss/typography` plugin like in v1, we now provide custom implementations of all prose components directly within Nuxt UI Pro. This gives us precise control over styling while ensuring perfect visual harmony with our design system. ::note{to="https://ui.nuxt.com/getting-started/typography"} Learn more about the new **Typography** system and all the available components. :: ## Utils Some utils will be **auto-imported** to make the bridge between `@nuxt/content` and `@nuxt/ui-pro`: ### `findPageHeadline` This util will allow you to bind an `headline` in the [PageHeader](https://ui.nuxt.com/components/page-header) based on the page `.navigation`. ```vue [pages/[...slug\\].vue] ``` ### `findPageBreadcrumb` This util will recursively find the breadcrumb of a page based on the navigation so you can use it in the [PageHeader](https://ui.nuxt.com/components/page-header) `#headline` slot. ```vue [app.vue] ``` ```vue [pages/[...slug\\].vue] ``` ### `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. As shown in the breadcrumb example above, it's commonly used to transform the navigation data into the correct format: ```vue [app.vue] ``` # Typography ## Usage When using Nuxt UI Pro v3 with [Nuxt Content v3](https://content.nuxt.com/){rel="nofollow"}, you get access to a set of pre-styled prose components. When using the [``](https://content.nuxt.com/docs/components/content-renderer){rel="nofollow"} component, your markdown content will be automatically styled with beautiful typography and consistent spacing. This includes headings, paragraphs, lists, tables, code blocks and more - no additional configuration required. ::note --- target: _blank to: https://content.nuxt.com/docs/getting-started/installation --- You can follow `@nuxt/content` installation guide to get started. :: ## Prose Components Prose components are replacements for HTML typography tags introduced by the [`@nuxtjs/mdc`](https://github.com/nuxt-modules/mdc){rel="nofollow"} module, Nuxt UI Pro overrides each one to provide a consistent look and feel. ::note{target="_blank" to="https://content.nuxt.com/docs/components/prose"} You can learn more about Prose components in the Nuxt Content documentation. :: ::tip You can use the prose components directly in your templates using the `Prose` prefix. ```vue ``` :: ### `h1` ::tabs :::code-preview --- className: - "[&>div]:*:my-0" label: Preview --- # Nuxt UI #code ```mdc # Nuxt UI ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `h2` ::tabs :::code-preview --- className: - "[&>div]:*:my-0" label: Preview --- ## What's new in v3? #code ```mdc ## What's new in v3? ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `h3` ::tabs :::code-preview --- className: - "[&>div]:*:my-0" label: Preview --- ### Tailwind CSS v4 #code ```mdc ### Tailwind CSS v4 ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `h4` ::tabs :::code-preview --- className: - "[&>div]:*:my-0" label: Preview --- #### Install Nuxt UI #code ```mdc #### Install Nuxt UI ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `p` ::tabs :::code-preview --- className: - "[&>div]:*:my-0" label: Preview --- Nuxt UI Pro is a collection of Vue components, composables and utils built on top of Nuxt UI, oriented on structure and layout and designed to be used as building blocks for your app. #code ```mdc Nuxt UI Pro is a collection of Vue components, composables and utils built on top of Nuxt UI, oriented on structure and layout and designed to be used as building blocks for your app. ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `a` ::tabs :::code-preview --- className: - "[&>div]:*:my-0" label: Preview --- [Nuxt documentation](https://nuxt.com){rel="nofollow"} #code ```mdc [Nuxt documentation](https://nuxt.com) ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `blockquote` ::tabs :::code-preview --- label: Preview --- > While Nuxt UI is free and open source, Nuxt UI Pro is a premium product that helps sustain Nuxt OSS development, check out the License section to learn more. #code ```mdc > While Nuxt UI is free and open source, Nuxt UI Pro is a premium product that helps sustain Nuxt OSS development, check out the License section to learn more. ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `strong` ::tabs :::code-preview --- className: - "[&>div]:*:my-0" label: Preview --- **Strong text** #code ```mdc **Strong text** ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `em` ::tabs :::code-preview --- className: - "[&>div]:*:my-0" label: Preview --- *Emphasized text* #code ```mdc *Emphasized text* ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `ul` ::tabs :::code-preview --- className: - "[&>div]:*:my-0" label: Preview --- - I'm a list item. - I'm another list item. - I'm the last list item. #code ```mdc - I'm a list item. - I'm another list item. - I'm the last list item. ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `ol` ::tabs :::code-preview --- className: - "[&>div]:*:my-0" label: Preview --- 1. I'm a list item. 2. I'm another list item. 3. I'm the last list item. #code ```mdc 1. I'm a list item. 2. I'm another list item. 3. I'm the last list item. ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `hr` ::tabs :::code-preview --- className: - "[&>div]:*:my-0" - "[&>div]:*:w-full" label: Preview --- --- #code ```mdc --- ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `table` ::tabs :::code-preview --- className: - "[&>div]:*:my-0" - "[&>div]:*:w-full" label: Preview --- | Prop | Default | Type | | ------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `name` | | `string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} | | `size` | `md` | `string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} | | `color` | `neutral` | `string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} | #code ```mdc | Prop | Default | Type | |---------|-----------|--------------------------| | `name` | | `string`{lang="ts-type"} | | `size` | `md` | `string`{lang="ts-type"} | | `color` | `neutral` | `string`{lang="ts-type"} | ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `img` ::code-preview ![Nuxt Social Image](https://nuxt.com/new-social.jpg) #code ```mdc ![Nuxt Social Image](https://nuxt.com/new-social.jpg) ``` :: ::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. :: ::tabs :::component-props{pro prose label="Props" name="img"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `code` ::code-preview --- className: - "[&>div]:*:my-0" --- `inline code` #code ```mdc `inline code` ``` :: You can use the `color` prop to specify the color of the code block. Defaults to `neutral`. ::code-preview --- className: - "[&>div]:*:my-0" --- `inline code`{color="error"} #code ```mdc `inline code`{color="error"} ``` :: You can use the `lang` prop to specify the language of the code block. ::code-preview --- className: - "[&>div]:*:my-0" label: Preview --- `nuxt.config.ts`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} #code ```mdc `nuxt.config.ts`{lang="ts-type"} ``` :: ::tabs :::component-props{pro prose label="Props" name="code"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `pre` Code-blocks are rendered by the `ProsePre` component of `@nuxtjs/mdc` and [code highlighting](https://content.nuxt.com/docs/files/markdown#code-highlighting){rel="nofollow"} is done underneath by [Shiki](https://github.com/shikijs/shiki){rel="nofollow"}. ::tip By default for syntax highlighting, `material-theme-lighter` and `material-theme-palenight` VSCode themes are used for light & dark mode respectively. You can change this in your `nuxt.config.ts` through the [`content.build.markdown.highlight`](https://content.nuxt.com/docs/getting-started/configuration#highlight){rel="nofollow"} key. :: ::code-preview --- className: - "[&>div]:*:my-0" - "[&>div]:*:w-full" --- ```ts export default defineNuxtConfig({ modules: ['@nuxt/ui-pro'] }) ``` #code ````mdc ```ts export default defineNuxtConfig({ modules: ['@nuxt/ui-pro'] }) ``` ```` :: When writing a code-block, you can specify a filename that will be displayed on top of the code block. An icon will be automatically displayed based on the extension or the name. ::code-preview --- className: - "[&>div]:*:my-0" - "[&>div]:*:w-full" --- ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/ui-pro'] }) ``` #code ````mdc ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/ui-pro'] }) ``` ```` :: ::tip Some icons are already defined by default, but you can add more in your `app.config.ts` through the `uiPro.prose.codeIcon` key: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: { codeIcon: { terminal: 'i-ph-terminal-window-duotone' } } } }) ``` :: Every code-block has a built-in copy button that will copy the code to your clipboard. ::tip You can change the icon in your `app.config.ts` through the `ui.icons.copy` and `ui.icons.copyCheck` keys: ```ts [app.config.ts] export default defineAppConfig({ ui: { icons: { copy: 'i-lucide-copy', copyCheck: 'i-lucide-copy-check' } } }) ``` :: ::tabs :::component-props{pro prose label="Props" name="pre"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ## Vue Components Nuxt UI Pro also provides a set of Vue components to help you write your content using the [MDC syntax](https://content.nuxt.com/docs/files/markdown#mdc-syntax){rel="nofollow"}. ### `Accordion` Use the `accordion` and `accordion-item` components to display an [Accordion](https://ui.nuxt.com/components/accordion) in your content. ::code-preview --- className: - "[&>div]:*:my-0" --- :::accordion ::::accordion-item --- icon: i-lucide-circle-help label: What are the main considerations when upgrading to Nuxt UI v3? --- The transition to v3 involves significant changes, including new component structures, updated theming approaches, and revised TypeScript definitions. We recommend a careful, incremental upgrade process, starting with thorough testing in a development environment. :::: ::::accordion-item --- icon: i-lucide-circle-help label: Is Nuxt UI v3 compatible with standalone Vue projects? --- Nuxt UI is now compatible with Vue! You can follow the [installation guide](https://ui.nuxt.com/getting-started/installation/vue) to get started. :::: ::::accordion-item{icon="i-lucide-circle-help" label="What about Nuxt UI Pro?"} We've also rebuilt Nuxt UI Pro from scratch and released a `v3.0.0-alpha.x` package but it only contains the components to build this documentation yet. This will be a free update, so the license you buy now will be valid for v3. We're actively working to finish the rewrite of all Nuxt UI Pro components. :::: ::: #code ```mdc ::accordion ::accordion-item{label="What are the main considerations when upgrading to Nuxt UI v3?" icon="i-lucide-circle-help"} The transition to v3 involves significant changes, including new component structures, updated theming approaches, and revised TypeScript definitions. We recommend a careful, incremental upgrade process, starting with thorough testing in a development environment. :: ::accordion-item{label="Is Nuxt UI v3 compatible with standalone Vue projects?" icon="i-lucide-circle-help"} Nuxt UI is now compatible with Vue! You can follow the [installation guide](/getting-started/installation/vue) to get started. :: ::accordion-item{label="What about Nuxt UI Pro?" icon="i-lucide-circle-help"} We've also rebuilt Nuxt UI Pro from scratch and released a `v3.0.0-alpha.x` package but it only contains the components to build this documentation yet. This will be a free update, so the license you buy now will be valid for v3. We're actively working to finish the rewrite of all Nuxt UI Pro components. :: :: ``` :: ::tabs :::component-props{pro prose label="Props" name="accordion"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `Badge` Use markdown in the default slot of the `badge` component to display a [Badge](https://ui.nuxt.com/components/badge) in your content. ::tabs :::code-preview --- label: Preview --- ::::badge **v3.0.0** :::: #code ```mdc ::badge **v3.0.0** :: ``` ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `Callout` Use markdown in the default slot of the `callout` component to add eye-catching context to your content. Use the `icon` and `color` props to customize it. You can also pass any property from the [``](https://nuxt.com/docs/api/components/nuxt-link){rel="nofollow"} component. ```vue ``` You can also use the `note`, `tip`, `warning` and `caution` shortcuts with pre-defined icons and colors. ::code-preview --- label: Preview --- :::div{className="flex,flex-col,gap-4,w-full"} ::::note{className="w-full,my-0"} Here's some additional information for you. :::: ::::tip{className="w-full,my-0"} Here's a helpful suggestion. :::: ::::warning{className="w-full,my-0"} Be careful with this action as it might have unexpected results. :::: ::::caution{className="w-full,my-0"} This action cannot be undone. :::: ::: #code ```mdc ::note Here's some additional information. :: ::tip Here's a helpful suggestion. :: ::warning Be careful with this action as it might have unexpected results. :: ::caution This action cannot be undone. :: ``` :: ### `Card` Use markdown in the default slot of the `card` component to highlight your content. Use the `title`, `icon` and `color` props to customize it. You can also pass any property from the [``](https://nuxt.com/docs/api/components/nuxt-link){rel="nofollow"}. ```vue ``` ### `CardGroup` Wrap your `card` components with the `card-group` component to group them together in a grid layout. ::code-preview :::card-group{className="w-full"} ::::card --- icon: i-simple-icons-github target: _blank title: Dashboard to: https://github.com/nuxt-ui-pro/dashboard --- A dashboard with multi-column layout. :::: ::::card --- icon: i-simple-icons-github target: _blank title: SaaS to: https://github.com/nuxt-ui-pro/saas --- A template with landing, pricing, docs and blog. :::: ::::card --- icon: i-simple-icons-github target: _blank title: Docs to: https://github.com/nuxt-ui-pro/docs --- A documentation with `@nuxt/content`. :::: ::::card --- icon: i-simple-icons-github target: _blank title: Landing to: https://github.com/nuxt-ui-pro/landing --- A landing page you can use as starting point. :::: ::: #code ```mdc ::card-group ::card --- title: Dashboard icon: i-simple-icons-github to: https://github.com/nuxt-ui-pro/dashboard target: _blank --- A dashboard with multi-column layout. :: ::card --- title: SaaS icon: i-simple-icons-github to: https://github.com/nuxt-ui-pro/saas target: _blank --- A template with landing, pricing, docs and blog. :: ::card --- title: Docs icon: i-simple-icons-github to: https://github.com/nuxt-ui-pro/docs target: _blank --- A documentation with `@nuxt/content`. :: ::card --- title: Landing icon: i-simple-icons-github to: https://github.com/nuxt-ui-pro/landing target: _blank --- A landing page you can use as starting point. :: :: ``` :: ::tabs ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `CodeCollapse` Wrap your code-block with a `code-collapse` component to display a collapsible code block. ::code-preview --- className: - "[&>div]:*:my-0" - "[&>div]:*:w-full" --- :::code-collapse{className="[&>div]:my-0"} ```css [main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; @theme static { --font-sans: 'Public Sans', sans-serif; --breakpoint-3xl: 1920px; --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; } ``` ::: #code ````mdc ::code-collapse ```css [main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; @theme static { --font-sans: 'Public Sans', sans-serif; --breakpoint-3xl: 1920px; --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; } ``` :: ```` :: ::tabs :::component-props{pro prose label="Props" name="code-collapse"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `CodeGroup` Wrap your code-blocks around a `code-group` component to group them together in tabs. ::code-preview --- className: - "[&>div]:*:my-0" - "[&>div]:*:w-full" --- :::code-group ```bash [pnpm] pnpm add @nuxt/ui-pro ``` ```bash [yarn] yarn add @nuxt/ui-pro ``` ```bash [npm] npm install @nuxt/ui-pro ``` ```bash [bun] bun add @nuxt/ui-pro ``` ::: #code ````mdc ::code-group ```bash [pnpm] pnpm add @nuxt/ui-pro ``` ```bash [yarn] yarn add @nuxt/ui-pro ``` ```bash [npm] npm install @nuxt/ui-pro ``` ```bash [bun] bun add @nuxt/ui-pro ``` :: ```` :: ::note{to="https://ui.nuxt.com/#pre"} Like the `ProsePre` component, the `CodeGroup` handles filenames, icons and copy button. :: ::tabs :::component-props{pro prose label="Props" name="code-group"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `CodePreview` Wrap a code-block with the `code-preview` component to display a preview of an MDC component and its code using the `code` slot. ::code-preview --- className: - "[&>div]:*:my-0" - "[&>div]:*:w-full" label: Preview --- :::code-preview --- className: - "[&>div]:*:my-0" --- `inline code` #code ```mdc `inline code` ``` ::: #code ````mdc ::code-preview `inline code` #code ```mdc `inline code` ``` :: ```` :: ::tabs :::component-props{pro prose label="Props" name="code-preview"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `CodeTree` Wrap your code-blocks with a `code-tree` component in any particular order to display a tree view of your files. ::code-preview --- className: - "[&>div]:*:my-0" - "[&>div]:*:w-full" --- :::code-tree{default-value="app/app.config.ts"} ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/ui-pro'], future: { compatibilityVersion: 4 }, css: ['~/assets/css/main.css'] }) ``` ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; ``` ```ts [app/app.config.ts] export default defineAppConfig({ ui: { colors: { primary: 'sky', colors: 'slate' } } }) ``` ```vue [app/app.vue] ``` ```json [package.json] { "name": "nuxt-app", "private": true, "type": "module", "scripts": { "build": "nuxt build", "dev": "nuxt dev", "generate": "nuxt generate", "preview": "nuxt preview", "postinstall": "nuxt prepare", "typecheck": "nuxt typecheck" }, "dependencies": { "@iconify-json/lucide": "^1.2.18", "@nuxt/ui-pro": "^3.0.0", "nuxt": "^3.16.0" }, "devDependencies": { "typescript": "^5.8.2", "vue-tsc": "^2.2.0" } } ``` ```json [tsconfig.json] { "extends": "./.nuxt/tsconfig.json" } ``` ````md [README.md] # Nuxt 3 Minimal Starter Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. ## Setup Make sure to install the dependencies: ```bash # npm npm install # pnpm pnpm install # yarn yarn install # bun bun install ``` ## Development Server Start the development server on `http://localhost:3000`: ```bash # npm npm run dev # pnpm pnpm run dev # yarn yarn dev # bun bun run dev ``` ## Production Build the application for production: ```bash # npm npm run build # pnpm pnpm run build # yarn yarn build # bun bun run build ``` Locally preview production build: ```bash # npm npm run preview # pnpm pnpm run preview # yarn yarn preview # bun bun run preview ``` Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. ```` ::: #code :::code-collapse{className="[&>div>pre]:rounded-t-none,[&>div]:my-0"} `````mdc ::code-tree{defaultValue="app/app.config.ts"} ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/ui-pro'], future: { compatibilityVersion: 4 }, css: ['~/assets/css/main.css'] }) ``` ```css [app/assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; ``` ```ts [app/app.config.ts] export default defineAppConfig({ ui: { colors: { primary: 'sky', colors: 'slate' } } }) ``` ```vue [app/app.vue] ``` ```json [package.json] { "name": "nuxt-app", "private": true, "type": "module", "scripts": { "build": "nuxt build", "dev": "nuxt dev", "generate": "nuxt generate", "preview": "nuxt preview", "postinstall": "nuxt prepare", "typecheck": "nuxt typecheck" }, "dependencies": { "@iconify-json/lucide": "^1.2.18", "@nuxt/ui-pro": "^3.0.0", "nuxt": "^3.16.0" }, "devDependencies": { "typescript": "^5.8.2", "vue-tsc": "^2.2.0" } } ``` ```json [tsconfig.json] { "extends": "./.nuxt/tsconfig.json" } ``` ````md [README.md] # Nuxt 3 Minimal Starter Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. ## Setup Make sure to install the dependencies: ```bash # npm npm install # pnpm pnpm install # yarn yarn install # bun bun install ``` ## Development Server Start the development server on `http://localhost:3000`: ```bash # npm npm run dev # pnpm pnpm run dev # yarn yarn dev # bun bun run dev ``` ## Production Build the application for production: ```bash # npm npm run build # pnpm pnpm run build # yarn yarn build # bun bun run build ``` Locally preview production build: ```bash # npm npm run preview # pnpm pnpm run preview # yarn yarn preview # bun bun run preview ``` Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. ```` :: ````` ::: :: ::note{to="https://ui.nuxt.com/#pre"} Like the `ProsePre` component, the `CodeTree` handles filenames, icons and copy button. :: ::tabs :::component-props{pro prose label="Props" name="code-tree"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `Collapsible` Wrap your content with the `collapsible` component to display a [Collapsible](https://ui.nuxt.com/components/collapsible) in your content. ::code-preview --- className: - "[&>div]:*:w-full" --- :::collapsible | Prop | Default | Type | | ------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `name` | | `string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} | | `size` | `md` | `string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} | | `color` | `neutral` | `string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} | ::: #code ```mdc ::collapsible | Prop | Default | Type | |---------|-----------|--------------------------| | `name` | | `string`{lang="ts-type"} | | `size` | `md` | `string`{lang="ts-type"} | | `color` | `neutral` | `string`{lang="ts-type"} | :: ``` :: ::tabs :::component-props{pro prose label="Props" name="collapsible"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `Field` A field, prop or parameter to display in your content. ::code-preview :::field{required className="w-full" name="name" required="true" type="string"} The `description` can be set as prop or in the default slot with full **markdown** support. ::: #code ```mdc ::field{name="name" type="string" required} The `description` can be set as prop or in the default slot with full **markdown** support. :: ``` :: ::tabs :::component-props{pro prose label="Props" name="field"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `FieldGroup` Group fields together in a list. ::code-preview :::field-group ::::field{name="analytics" type="boolean"} Default to `false` - Enables analytics for your project (coming soon). :::: ::::field{name="blob" type="boolean"} Default to `false` - Enables blob storage to store static assets, such as images, videos and more. :::: ::::field{name="cache" type="boolean"} Default to `false` - Enables cache storage to cache your server route responses or functions using Nitro's `cachedEventHandler` and `cachedFunction` :::: ::::field{name="database" type="boolean"} Default to `false` - Enables SQL database to store your application's data. :::: ::: #code ```mdc ::field-group ::field{name="analytics" type="boolean"} Default to `false` - Enables analytics for your project (coming soon). :: ::field{name="blob" type="boolean"} Default to `false` - Enables blob storage to store static assets, such as images, videos and more. :: ::field{name="cache" type="boolean"} Default to `false` - Enables cache storage to cache your server route responses or functions using Nitro's `cachedEventHandler` and `cachedFunction` :: ::field{name="database" type="boolean"} Default to `false` - Enables SQL database to store your application's data. :: :: ``` :: ::tabs ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `Icon` Use the `icon` component to display an [Icon](https://ui.nuxt.com/components/icon) in your content. ::code-preview :::icon{name="i-simple-icons-nuxtdotjs"} ::: #code ```mdc :icon{name="i-simple-icons-nuxtdotjs"} ``` :: ::tabs :::component-props{pro prose label="Props" name="icon"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `Kbd` Use the `kbd` component to display a [Kbd](https://ui.nuxt.com/components/kbd) in your content. ::code-preview --- className: - "[&>div]:*:my-0" --- `` `` #code ```mdc :kbd{value="meta"} :kbd{value="K"} ``` :: ::tabs :::component-props{pro prose label="Props" name="kbd"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `Tabs` Use the `tabs` and `tabs-item` components to display [Tabs](https://ui.nuxt.com/components/tabs) in your content. ::code-preview --- className: - "[&>div]:*:my-0" --- :::tabs{className="w-full"} ::::tabs-item{icon="i-lucide-code" label="Code"} ```mdc ::callout Lorem velit voluptate ex reprehenderit ullamco et culpa. :: ``` :::: ::::tabs-item{icon="i-lucide-eye" label="Preview"} :::::callout Lorem velit voluptate ex reprehenderit ullamco et culpa. ::::: :::: ::: #code ````mdc ::tabs :::tabs-item{label="Code" icon="i-lucide-code"} ```mdc ::callout Lorem velit voluptate ex reprehenderit ullamco et culpa. :: ``` ::: :::tabs-item{label="Preview" icon="i-lucide-eye"} ::callout Lorem velit voluptate ex reprehenderit ullamco et culpa. :: ::: :: ```` :: ::tabs :::component-props{pro prose label="Props" name="tabs"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: ### `Steps` Wrap your headings with the Steps component to display a list of steps. Use the `level` prop to define which heading will be used for the steps. ::code-preview :::steps{level="4"} #### Add the Nuxt UI Pro module in your `nuxt.config.ts`{className="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-pro'] }) ``` #### Import Tailwind CSS and Nuxt UI Pro in your CSS ```css [assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; ``` ::: #code ````mdc ::steps{level="4"} #### Add the Nuxt UI Pro module in your `nuxt.config.ts`{lang="ts-type"} ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/ui-pro'] }) ``` #### Import Tailwind CSS and Nuxt UI Pro in your CSS ```css [assets/css/main.css] @import "tailwindcss"; @import "@nuxt/ui-pro"; ``` :: ```` :: ::tabs :::component-props{pro prose label="Props" name="steps"} ::: ```ts [app.config.ts] export default defineAppConfig({ uiPro: { prose: {} } }) ``` :: # Contribution Guide 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%20label%3Av3){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` v3 to generate pages from Markdown files. See the [Content v3 Docs](https://content3.nuxt.dev/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 - `--pro` Create a pro component - `--prose` Create a prose component (requires `--pro`) - `--content` Create a content component (requires `--pro`) - `--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 pro component nuxt-ui make component page-section --pro # Create a pro prose component nuxt-ui make component heading --pro --prose # Create a pro content component nuxt-ui make component block --pro --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/getting-started/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 v3 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` when running the tests. :: ### 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/v3/.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! ❤️ # 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. Use it at the root of your app: ```vue [app.vue] ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/i18n/nuxt#locale"} Learn how to use the `locale` prop to change the locale of your app. ::: #vue :::tip{to="https://ui.nuxt.com/getting-started/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; /** * 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; } ``` # Accordion ## Usage ### Items Use the `items` prop as an array of objects with the following properties: - `label?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `icon?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `trailingIcon?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `content?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `value?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `disabled?: boolean`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - [`slot?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-custom-slot) ```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/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/getting-started/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/getting-started/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 body slot Use the `#body` slot to customize the body of each item. ```vue [AccordionBodySlotExample.vue] ``` ::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] ``` ### With custom slot Use the `slot` property to customize a specific item. You will have access to the following slots: - `#{{ item.slot }}`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-body`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} ```vue [AccordionCustomSlotExample.vue] ``` ### 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. The `useSortable` composable accepts various options, see the [Usage](https://vueuse.org/integrations/useSortable/#usage){rel="nofollow"} for more examples. ```vue [AccordionDragAndDropExample.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 | undefined; /** * The key used to get the label from the item. * @default "\"label\"" */ labelKey?: string | 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-(--ui-border) 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-(--ui-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' } } } } } }) ``` # 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/components/icon). ```vue ``` ### Avatar Use the `avatar` prop to show an [Avatar](https://ui.nuxt.com/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/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/components/button) component to customize it. ```vue ``` ### Close Icon Use the `close-icon` prop to customize the close button [Icon](https://ui.nuxt.com/components/icon). Defaults to `i-lucide-x`. ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/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/getting-started/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/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 | 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 | 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-(--ui-primary) text-(--ui-bg)' } }, { color: 'secondary', variant: 'solid', class: { root: 'bg-(--ui-secondary) text-(--ui-bg)' } }, { color: 'success', variant: 'solid', class: { root: 'bg-(--ui-success) text-(--ui-bg)' } }, { color: 'info', variant: 'solid', class: { root: 'bg-(--ui-info) text-(--ui-bg)' } }, { color: 'warning', variant: 'solid', class: { root: 'bg-(--ui-warning) text-(--ui-bg)' } }, { color: 'error', variant: 'solid', class: { root: 'bg-(--ui-error) text-(--ui-bg)' } }, { color: 'primary', variant: 'outline', class: { root: 'text-(--ui-primary) ring ring-inset ring-(--ui-primary)/25' } }, { color: 'secondary', variant: 'outline', class: { root: 'text-(--ui-secondary) ring ring-inset ring-(--ui-secondary)/25' } }, { color: 'success', variant: 'outline', class: { root: 'text-(--ui-success) ring ring-inset ring-(--ui-success)/25' } }, { color: 'info', variant: 'outline', class: { root: 'text-(--ui-info) ring ring-inset ring-(--ui-info)/25' } }, { color: 'warning', variant: 'outline', class: { root: 'text-(--ui-warning) ring ring-inset ring-(--ui-warning)/25' } }, { color: 'error', variant: 'outline', class: { root: 'text-(--ui-error) ring ring-inset ring-(--ui-error)/25' } }, { color: 'primary', variant: 'soft', class: { root: 'bg-(--ui-primary)/10 text-(--ui-primary)' } }, { color: 'secondary', variant: 'soft', class: { root: 'bg-(--ui-secondary)/10 text-(--ui-secondary)' } }, { color: 'success', variant: 'soft', class: { root: 'bg-(--ui-success)/10 text-(--ui-success)' } }, { color: 'info', variant: 'soft', class: { root: 'bg-(--ui-info)/10 text-(--ui-info)' } }, { color: 'warning', variant: 'soft', class: { root: 'bg-(--ui-warning)/10 text-(--ui-warning)' } }, { color: 'error', variant: 'soft', class: { root: 'bg-(--ui-error)/10 text-(--ui-error)' } }, { color: 'primary', variant: 'subtle', class: { root: 'bg-(--ui-primary)/10 text-(--ui-primary) ring ring-inset ring-(--ui-primary)/25' } }, { color: 'secondary', variant: 'subtle', class: { root: 'bg-(--ui-secondary)/10 text-(--ui-secondary) ring ring-inset ring-(--ui-secondary)/25' } }, { color: 'success', variant: 'subtle', class: { root: 'bg-(--ui-success)/10 text-(--ui-success) ring ring-inset ring-(--ui-success)/25' } }, { color: 'info', variant: 'subtle', class: { root: 'bg-(--ui-info)/10 text-(--ui-info) ring ring-inset ring-(--ui-info)/25' } }, { color: 'warning', variant: 'subtle', class: { root: 'bg-(--ui-warning)/10 text-(--ui-warning) ring ring-inset ring-(--ui-warning)/25' } }, { color: 'error', variant: 'subtle', class: { root: 'bg-(--ui-error)/10 text-(--ui-error) ring ring-inset ring-(--ui-error)/25' } }, { color: 'neutral', variant: 'solid', class: { root: 'text-(--ui-bg) bg-(--ui-bg-inverted)' } }, { color: 'neutral', variant: 'outline', class: { root: 'text-(--ui-text-highlighted) bg-(--ui-bg) ring ring-inset ring-(--ui-border)' } }, { color: 'neutral', variant: 'soft', class: { root: 'text-(--ui-text-highlighted) bg-(--ui-bg-elevated)/50' } }, { color: 'neutral', variant: 'subtle', class: { root: 'text-(--ui-text-highlighted) bg-(--ui-bg-elevated)/50 ring ring-inset ring-(--ui-border-accented)' } } ], defaultVariants: { color: 'primary', variant: 'solid' } } } }) ``` # AuthForm ## Usage Built on top of the [Form](https://ui.nuxt.com/components/form) component, the `AuthForm` component can be used in your pages or wrapped in a [PageCard](https://ui.nuxt.com/components/page-card). The form will construct itself based on the `fields` prop and the state will be handled internally. You can pass all the props you would pass to a [FormField](https://ui.nuxt.com/components/form-field#props) or an [Input](https://ui.nuxt.com/components/input#props) to each field. ```vue [AuthFormExample.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/components/button) component such as `variant`, `color`, `to`, etc. ```vue ``` ### Separator Use the `separator` prop to customize the [Separator](https://ui.nuxt.com/components/separator) between the providers and the fields. Defaults to `or`. ```vue ``` You can pass any property from the [Separator](https://ui.nuxt.com/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/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/components/page-card) component to display it within a `login.vue` page for example. ```vue [AuthFormPageExample.vue] ``` ## API ### Props ```ts /** * Props for the AuthForm component */ interface AuthFormProps { } ``` ### Slots ```ts /** * Slots for the AuthForm component */ interface AuthFormSlots { header(): any; leading(): any; title(): any; description(): any; validation(): any; footer(): any; } ``` ### Emits ```ts /** * Emitted events for the AuthForm component */ interface AuthFormEmits { submit: (payload: FormSubmitEvent) => void; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { authForm: { slots: { root: 'w-full space-y-6', header: 'flex flex-col text-center', leading: 'mb-2', leadingIcon: 'size-8 shrink-0', title: 'text-xl text-pretty font-semibold text-(--ui-text-highlighted)', description: 'mt-1 text-base text-pretty text-(--ui-text-muted)', body: 'gap-y-6 flex flex-col', providers: 'space-y-3', separator: '', form: 'space-y-5', footer: 'text-sm text-center text-(--ui-text-muted) mt-2' } } } }) ``` # Avatar ## Usage The Avatar uses the `` component when [`@nuxt/image`](https://github.com/nuxt/image){rel="nofollow"} is installed, falling back to `img` otherwise. ::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/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. :: ## Examples ### With tooltip You can use a [Tooltip](https://ui.nuxt.com/components/tooltip) component to display a tooltip when hovering the Avatar. ```vue [AvatarTooltipExample.vue] ``` ### With chip You can use a [Chip](https://ui.nuxt.com/components/chip) component to display a chip around the Avatar. ```vue [AvatarChipExample.vue] ``` ## API ### Props ```ts /** * Props for the Avatar component */ interface AvatarProps { /** * The element or component this component should render as. * @default "\"span\"" */ as?: any; src?: string | undefined; alt?: string | undefined; icon?: string | undefined; text?: string | undefined; size?: "md" | "3xs" | "2xs" | "xs" | "sm" | "lg" | "xl" | "2xl" | "3xl" | 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 overflow-hidden rounded-full align-middle bg-(--ui-bg-elevated)', image: 'h-full w-full rounded-[inherit] object-cover', fallback: 'font-medium leading-none text-(--ui-text-muted) truncate', icon: 'text-(--ui-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' } } } }) ``` # AvatarGroup ## Usage Wrap multiple [Avatar](https://ui.nuxt.com/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/components/tooltip) to display a tooltip on hover. ```vue [AvatarGroupTooltipExample.vue] ``` ### With chip Wrap each avatar with a [Chip](https://ui.nuxt.com/components/chip) to display a chip around the avatar. ```vue [AvatarGroupChipExample.vue] ``` ### With link Wrap each avatar with a [Link](https://ui.nuxt.com/components/link) to make them clickable. ```vue [AvatarGroupLinkExample.vue] ``` ## API ### Props ```ts /** * Props for the AvatarGroup component */ interface AvatarGroupProps { /** * The element or component this component should render as. */ as?: any; size?: "md" | "3xs" | "2xs" | "xs" | "sm" | "lg" | "xl" | "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-(--ui-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' } } } }) ``` # Badge ## Usage ### Label Use the default slot to set the label of the Badge. ```vue ``` You can achieve the same result by using the `label` prop. ```vue ``` ### Color Use the `color` prop to change the color of the Badge. ```vue ``` ### Variant Use the `variant` props to change the variant of the Badge. ```vue ``` ### Size Use the `size` prop to change the size of the Badge. ```vue ``` ### Icon Use the `icon` prop to show an [Icon](https://ui.nuxt.com/components/icon) inside the Badge. ```vue ``` 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 ``` ### Avatar Use the `avatar` prop to show an [Avatar](https://ui.nuxt.com/components/avatar) inside the Badge. ```vue ``` ## Examples ### `class` prop Use the `class` prop to override the base styles of the Badge. ```vue ``` ## 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?: "md" | "xs" | "sm" | "lg" | "xl" | 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 | 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 | undefined; /** * When `true`, the icon will be displayed on the right side. */ trailing?: boolean | undefined; /** * Display an icon on the right side. */ trailingIcon?: string | 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: { buttonGroup: { horizontal: 'not-only:first:rounded-e-none not-only:last:rounded-s-none not-last:not-first:rounded-none', vertical: 'not-only:first:rounded-b-none not-only:last:rounded-t-none not-last:not-first:rounded-none' }, 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' } } }, compoundVariants: [ { color: 'primary', variant: 'solid', class: 'bg-(--ui-primary) text-(--ui-bg)' }, { color: 'secondary', variant: 'solid', class: 'bg-(--ui-secondary) text-(--ui-bg)' }, { color: 'success', variant: 'solid', class: 'bg-(--ui-success) text-(--ui-bg)' }, { color: 'info', variant: 'solid', class: 'bg-(--ui-info) text-(--ui-bg)' }, { color: 'warning', variant: 'solid', class: 'bg-(--ui-warning) text-(--ui-bg)' }, { color: 'error', variant: 'solid', class: 'bg-(--ui-error) text-(--ui-bg)' }, { color: 'primary', variant: 'outline', class: 'text-(--ui-primary) ring ring-inset ring-(--ui-primary)/50' }, { color: 'secondary', variant: 'outline', class: 'text-(--ui-secondary) ring ring-inset ring-(--ui-secondary)/50' }, { color: 'success', variant: 'outline', class: 'text-(--ui-success) ring ring-inset ring-(--ui-success)/50' }, { color: 'info', variant: 'outline', class: 'text-(--ui-info) ring ring-inset ring-(--ui-info)/50' }, { color: 'warning', variant: 'outline', class: 'text-(--ui-warning) ring ring-inset ring-(--ui-warning)/50' }, { color: 'error', variant: 'outline', class: 'text-(--ui-error) ring ring-inset ring-(--ui-error)/50' }, { color: 'primary', variant: 'soft', class: 'bg-(--ui-primary)/10 text-(--ui-primary)' }, { color: 'secondary', variant: 'soft', class: 'bg-(--ui-secondary)/10 text-(--ui-secondary)' }, { color: 'success', variant: 'soft', class: 'bg-(--ui-success)/10 text-(--ui-success)' }, { color: 'info', variant: 'soft', class: 'bg-(--ui-info)/10 text-(--ui-info)' }, { color: 'warning', variant: 'soft', class: 'bg-(--ui-warning)/10 text-(--ui-warning)' }, { color: 'error', variant: 'soft', class: 'bg-(--ui-error)/10 text-(--ui-error)' }, { color: 'primary', variant: 'subtle', class: 'bg-(--ui-primary)/10 text-(--ui-primary) ring ring-inset ring-(--ui-primary)/25' }, { color: 'secondary', variant: 'subtle', class: 'bg-(--ui-secondary)/10 text-(--ui-secondary) ring ring-inset ring-(--ui-secondary)/25' }, { color: 'success', variant: 'subtle', class: 'bg-(--ui-success)/10 text-(--ui-success) ring ring-inset ring-(--ui-success)/25' }, { color: 'info', variant: 'subtle', class: 'bg-(--ui-info)/10 text-(--ui-info) ring ring-inset ring-(--ui-info)/25' }, { color: 'warning', variant: 'subtle', class: 'bg-(--ui-warning)/10 text-(--ui-warning) ring ring-inset ring-(--ui-warning)/25' }, { color: 'error', variant: 'subtle', class: 'bg-(--ui-error)/10 text-(--ui-error) ring ring-inset ring-(--ui-error)/25' }, { color: 'neutral', variant: 'solid', class: 'text-(--ui-bg) bg-(--ui-bg-inverted)' }, { color: 'neutral', variant: 'outline', class: 'ring ring-inset ring-(--ui-border-accented) text-(--ui-text) bg-(--ui-bg)' }, { color: 'neutral', variant: 'soft', class: 'text-(--ui-text) bg-(--ui-bg-elevated)' }, { color: 'neutral', variant: 'subtle', class: 'ring ring-inset ring-(--ui-border-accented) text-(--ui-text) bg-(--ui-bg-elevated)' } ], defaultVariants: { color: 'primary', variant: 'solid', size: 'md' } } } }) ``` # 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/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/components/icon). Defaults to `i-lucide-x`. ```vue [BannerExample.vue] ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/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/getting-started/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/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 { } ``` ### Slots ```ts /** * Slots for the Banner component */ interface BannerSlots { } ``` ### Emits No events available for this component. ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { 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-(--ui-bg) pointer-events-none', title: 'text-sm text-(--ui-bg) font-medium truncate', actions: 'flex gap-1.5 shrink-0 isolate', close: 'text-(--ui-bg) hover:bg-(--ui-bg)/10 focus-visible:bg-(--ui-bg)/10 -me-1.5 lg:me-0' }, variants: { color: { primary: { root: 'bg-(--ui-primary)' }, secondary: { root: 'bg-(--ui-secondary)' }, success: { root: 'bg-(--ui-success)' }, info: { root: 'bg-(--ui-info)' }, warning: { root: 'bg-(--ui-warning)' }, error: { root: 'bg-(--ui-error)' }, neutral: { root: 'bg-(--ui-bg-inverted)' } }, to: { true: '' } }, compoundVariants: [ { color: 'primary', to: true, class: { root: 'hover:bg-(--ui-primary)/90' } }, { color: 'secondary', to: true, class: { root: 'hover:bg-(--ui-secondary)/90' } }, { color: 'success', to: true, class: { root: 'hover:bg-(--ui-success)/90' } }, { color: 'info', to: true, class: { root: 'hover:bg-(--ui-info)/90' } }, { color: 'warning', to: true, class: { root: 'hover:bg-(--ui-warning)/90' } }, { color: 'error', to: true, class: { root: 'hover:bg-(--ui-error)/90' } }, { color: 'neutral', to: true, class: { root: 'hover:bg-(--ui-bg-inverted)/90' } } ], defaultVariants: { color: 'primary' } } } }) ``` # 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 className: - 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/components/blog-posts"} Use the [`BlogPosts`](https://ui.nuxt.com/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/getting-started/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/components/badge) in the BlogPost. ```vue ``` You can pass any property from the [Badge](https://ui.nuxt.com/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/components/user) in the BlogPost as an array of objects with the following properties: - `name?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `description?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `avatar?: Omit`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `chip?: boolean | Omit`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `size?: UserProps['size']`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `orientation?: UserProps['orientation']`{className="language-ts-type 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/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/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 { /** * @default "\"vertical\"" */ orientation?: "vertical" | "horizontal" | undefined; /** * @default "\"article\"" */ as?: any; ui?: { root?: ClassNameValue; header?: ClassNameValue; body?: ClassNameValue; footer?: ClassNameValue; image?: ClassNameValue; ... 6 more ...; badge?: ClassNameValue; } | undefined; target?: "_blank" | "_parent" | "_self" | "_top" | (string & {}) | null | undefined; to?: string | RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric | undefined; title?: string | undefined; description?: string | 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; variant?: "outline" | "soft" | "subtle" | "ghost" | "naked" | undefined; /** * The image of the blog post. Can be a string or an object. */ image?: string | Partial | undefined; /** * The date of the blog post. Can be a string or a Date object. */ date?: string | Date | undefined; /** * The authors of the blog post. */ authors?: UserProps[] | 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({ uiPro: { blogPost: { slots: { root: 'relative group/blog-post flex flex-col rounded-[calc(var(--ui-radius)*2)] 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-(--ui-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-(--ui-bg) ring ring-(--ui-border)', date: 'text-(--ui-text-toned)', description: 'text-(--ui-text-muted)' }, soft: { root: 'bg-(--ui-bg-elevated)/50', date: 'text-(--ui-text-muted)', description: 'text-(--ui-text-toned)' }, subtle: { root: 'bg-(--ui-bg-elevated)/50 ring ring-(--ui-border)', date: 'text-(--ui-text-muted)', description: 'text-(--ui-text-toned)' }, ghost: { date: 'text-(--ui-text-toned)', description: 'text-(--ui-text-muted)', header: 'shadow-lg rounded-[calc(var(--ui-radius)*2)]' }, naked: { root: 'p-0 sm:p-0', date: 'text-(--ui-text-toned)', description: 'text-(--ui-text-muted)', header: 'shadow-lg rounded-[calc(var(--ui-radius)*2)]' } }, 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-(--ui-bg-elevated)/50' } }, { variant: 'soft', to: true, class: { root: 'hover:bg-(--ui-bg-elevated)' } }, { variant: 'subtle', to: true, class: { root: 'hover:bg-(--ui-bg-elevated) hover:ring-(--ui-border-accented)' } }, { variant: 'ghost', to: true, class: { root: 'hover:bg-(--ui-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' } } } }) ``` # BlogPosts ## Usage The BlogPosts component provides a flexible layout to display a list of [BlogPost](https://ui.nuxt.com/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/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 { /** * @default "\"horizontal\"" */ orientation?: "vertical" | "horizontal" | undefined; /** * The element or component this component should render as. */ as?: any; posts?: BlogPostProps[] | undefined; } ``` ### Slots ```ts /** * Slots for the BlogPosts component */ interface BlogPostsSlots { default(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { 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: '' } } } } }) ``` # Breadcrumb ## Usage ### Items Use the `items` prop as an array of objects with the following properties: - `label?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `icon?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `avatar?: AvatarProps`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `class?: any`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - [`slot?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-custom-slot) You can pass any property from the [Link](https://ui.nuxt.com/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/components/icon) between each item. Defaults to `i-lucide-chevron-right`. ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/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/getting-started/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 }}`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-leading`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-label`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-trailing`{className="language-ts-type 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 | undefined; /** * The key used to get the label from the item. * @default "\"label\"" */ labelKey?: string | undefined; ui?: { root?: ClassNameValue; list?: ClassNameValue; item?: ClassNameValue; link?: ClassNameValue; linkLeadingIcon?: ClassNameValue; ... 4 more ...; 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-(--ui-primary)', linkLeadingIcon: 'shrink-0 size-5', linkLeadingAvatar: 'shrink-0', linkLeadingAvatarSize: '2xs', linkLabel: 'truncate', separator: 'flex', separatorIcon: 'shrink-0 size-5 text-(--ui-text-muted)' }, variants: { active: { true: { link: 'text-(--ui-primary) font-semibold' }, false: { link: 'text-(--ui-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-(--ui-text)', 'transition-colors' ] } } ] } } }) ``` # Button ## Usage ### Label Use the default slot to set the label of the Button. ```vue ``` You can achieve the same result by using the `label` prop. ```vue ``` ### Color Use the `color` prop to change the color of the Button. ```vue ``` ### Variant Use the `variant` prop to change the variant of the Button. ```vue ``` ### Size Use the `size` prop to change the size of the Button. ```vue ``` ### Icon Use the `icon` prop to show an [Icon](https://ui.nuxt.com/components/icon) inside the Button. ```vue ``` 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 ``` 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/components/avatar) inside the Button. ```vue ``` 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/components/link#props) component such as `to`, `target`, etc. ```vue ``` 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 ``` You can also use the `active-class` and `inactive-class` props to customize the active state. ```vue ``` ::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 ``` Use the `loading-auto` prop to show the loading icon automatically while the `@click` promise is pending. ```vue [ButtonLoadingAutoExample.vue] ``` This also works with the [Form](https://ui.nuxt.com/components/form) component. ```vue [ButtonLoadingAutoFormExample.vue] ``` ### Loading Icon Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-refresh-cw`. ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/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/getting-started/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 ``` ## Examples ### `class` prop Use the `class` prop to override the base styles of the Button. ```vue ``` ### `ui` prop Use the `ui` prop to override the slots styles of the Button. ```vue ``` ## 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?: "link" | "solid" | "outline" | "soft" | "subtle" | "ghost" | undefined; activeVariant?: "link" | "solid" | "outline" | "soft" | "subtle" | "ghost" | undefined; size?: "md" | "xs" | "sm" | "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; 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 | 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 | undefined; /** * When `true`, the icon will be displayed on the right side. */ trailing?: boolean | undefined; /** * Display an icon on the right side. */ trailingIcon?: string | undefined; /** * When `true`, the loading icon will be displayed. */ loading?: boolean | undefined; /** * The icon when the `loading` prop is `true`. */ loadingIcon?: string | 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; /** * Calls `router.replace` instead of `router.push`. */ replace?: boolean | undefined; /** * Route Location the link should navigate to when clicked on. */ to?: string | RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric | undefined; /** * Class to apply when the link is active * @default "\"\"" */ 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?: "true" | "false" | "page" | "step" | "location" | "date" | "time" | undefined; /** * Pass the returned promise of `router.push()` to `document.startViewTransition()` if supported. */ viewTransition?: boolean | undefined; disabled?: boolean | undefined; /** * Force the link to be active independent of the current route. * @default "undefined" */ 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. * @default "\"\"" */ 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/v3/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: { buttonGroup: { horizontal: 'not-only:first:rounded-e-none not-only:last:rounded-s-none not-last:not-first:rounded-none', vertical: 'not-only:first:rounded-b-none not-only:last:rounded-t-none not-last:not-first:rounded-none' }, 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-(--ui-bg) bg-(--ui-primary) hover:bg-(--ui-primary)/75 disabled:bg-(--ui-primary) aria-disabled:bg-(--ui-primary) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-primary)' }, { color: 'secondary', variant: 'solid', class: 'text-(--ui-bg) bg-(--ui-secondary) hover:bg-(--ui-secondary)/75 disabled:bg-(--ui-secondary) aria-disabled:bg-(--ui-secondary) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-secondary)' }, { color: 'success', variant: 'solid', class: 'text-(--ui-bg) bg-(--ui-success) hover:bg-(--ui-success)/75 disabled:bg-(--ui-success) aria-disabled:bg-(--ui-success) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-success)' }, { color: 'info', variant: 'solid', class: 'text-(--ui-bg) bg-(--ui-info) hover:bg-(--ui-info)/75 disabled:bg-(--ui-info) aria-disabled:bg-(--ui-info) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-info)' }, { color: 'warning', variant: 'solid', class: 'text-(--ui-bg) bg-(--ui-warning) hover:bg-(--ui-warning)/75 disabled:bg-(--ui-warning) aria-disabled:bg-(--ui-warning) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-warning)' }, { color: 'error', variant: 'solid', class: 'text-(--ui-bg) bg-(--ui-error) hover:bg-(--ui-error)/75 disabled:bg-(--ui-error) aria-disabled:bg-(--ui-error) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-error)' }, { color: 'primary', variant: 'outline', class: 'ring ring-inset ring-(--ui-primary)/50 text-(--ui-primary) hover:bg-(--ui-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-(--ui-primary)' }, { color: 'secondary', variant: 'outline', class: 'ring ring-inset ring-(--ui-secondary)/50 text-(--ui-secondary) hover:bg-(--ui-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-(--ui-secondary)' }, { color: 'success', variant: 'outline', class: 'ring ring-inset ring-(--ui-success)/50 text-(--ui-success) hover:bg-(--ui-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-(--ui-success)' }, { color: 'info', variant: 'outline', class: 'ring ring-inset ring-(--ui-info)/50 text-(--ui-info) hover:bg-(--ui-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-(--ui-info)' }, { color: 'warning', variant: 'outline', class: 'ring ring-inset ring-(--ui-warning)/50 text-(--ui-warning) hover:bg-(--ui-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-(--ui-warning)' }, { color: 'error', variant: 'outline', class: 'ring ring-inset ring-(--ui-error)/50 text-(--ui-error) hover:bg-(--ui-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-(--ui-error)' }, { color: 'primary', variant: 'soft', class: 'text-(--ui-primary) bg-(--ui-primary)/10 hover:bg-(--ui-primary)/15 focus:outline-none focus-visible:bg-(--ui-primary)/15 disabled:bg-(--ui-primary)/10 aria-disabled:bg-(--ui-primary)/10' }, { color: 'secondary', variant: 'soft', class: 'text-(--ui-secondary) bg-(--ui-secondary)/10 hover:bg-(--ui-secondary)/15 focus:outline-none focus-visible:bg-(--ui-secondary)/15 disabled:bg-(--ui-secondary)/10 aria-disabled:bg-(--ui-secondary)/10' }, { color: 'success', variant: 'soft', class: 'text-(--ui-success) bg-(--ui-success)/10 hover:bg-(--ui-success)/15 focus:outline-none focus-visible:bg-(--ui-success)/15 disabled:bg-(--ui-success)/10 aria-disabled:bg-(--ui-success)/10' }, { color: 'info', variant: 'soft', class: 'text-(--ui-info) bg-(--ui-info)/10 hover:bg-(--ui-info)/15 focus:outline-none focus-visible:bg-(--ui-info)/15 disabled:bg-(--ui-info)/10 aria-disabled:bg-(--ui-info)/10' }, { color: 'warning', variant: 'soft', class: 'text-(--ui-warning) bg-(--ui-warning)/10 hover:bg-(--ui-warning)/15 focus:outline-none focus-visible:bg-(--ui-warning)/15 disabled:bg-(--ui-warning)/10 aria-disabled:bg-(--ui-warning)/10' }, { color: 'error', variant: 'soft', class: 'text-(--ui-error) bg-(--ui-error)/10 hover:bg-(--ui-error)/15 focus:outline-none focus-visible:bg-(--ui-error)/15 disabled:bg-(--ui-error)/10 aria-disabled:bg-(--ui-error)/10' }, { color: 'primary', variant: 'subtle', class: 'text-(--ui-primary) ring ring-inset ring-(--ui-primary)/25 bg-(--ui-primary)/10 hover:bg-(--ui-primary)/15 disabled:bg-(--ui-primary)/10 aria-disabled:bg-(--ui-primary)/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-(--ui-primary)' }, { color: 'secondary', variant: 'subtle', class: 'text-(--ui-secondary) ring ring-inset ring-(--ui-secondary)/25 bg-(--ui-secondary)/10 hover:bg-(--ui-secondary)/15 disabled:bg-(--ui-secondary)/10 aria-disabled:bg-(--ui-secondary)/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-(--ui-secondary)' }, { color: 'success', variant: 'subtle', class: 'text-(--ui-success) ring ring-inset ring-(--ui-success)/25 bg-(--ui-success)/10 hover:bg-(--ui-success)/15 disabled:bg-(--ui-success)/10 aria-disabled:bg-(--ui-success)/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-(--ui-success)' }, { color: 'info', variant: 'subtle', class: 'text-(--ui-info) ring ring-inset ring-(--ui-info)/25 bg-(--ui-info)/10 hover:bg-(--ui-info)/15 disabled:bg-(--ui-info)/10 aria-disabled:bg-(--ui-info)/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-(--ui-info)' }, { color: 'warning', variant: 'subtle', class: 'text-(--ui-warning) ring ring-inset ring-(--ui-warning)/25 bg-(--ui-warning)/10 hover:bg-(--ui-warning)/15 disabled:bg-(--ui-warning)/10 aria-disabled:bg-(--ui-warning)/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-(--ui-warning)' }, { color: 'error', variant: 'subtle', class: 'text-(--ui-error) ring ring-inset ring-(--ui-error)/25 bg-(--ui-error)/10 hover:bg-(--ui-error)/15 disabled:bg-(--ui-error)/10 aria-disabled:bg-(--ui-error)/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-(--ui-error)' }, { color: 'primary', variant: 'ghost', class: 'text-(--ui-primary) hover:bg-(--ui-primary)/10 focus:outline-none focus-visible:bg-(--ui-primary)/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent' }, { color: 'secondary', variant: 'ghost', class: 'text-(--ui-secondary) hover:bg-(--ui-secondary)/10 focus:outline-none focus-visible:bg-(--ui-secondary)/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent' }, { color: 'success', variant: 'ghost', class: 'text-(--ui-success) hover:bg-(--ui-success)/10 focus:outline-none focus-visible:bg-(--ui-success)/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent' }, { color: 'info', variant: 'ghost', class: 'text-(--ui-info) hover:bg-(--ui-info)/10 focus:outline-none focus-visible:bg-(--ui-info)/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent' }, { color: 'warning', variant: 'ghost', class: 'text-(--ui-warning) hover:bg-(--ui-warning)/10 focus:outline-none focus-visible:bg-(--ui-warning)/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent' }, { color: 'error', variant: 'ghost', class: 'text-(--ui-error) hover:bg-(--ui-error)/10 focus:outline-none focus-visible:bg-(--ui-error)/10 disabled:bg-transparent aria-disabled:bg-transparent dark:disabled:bg-transparent dark:aria-disabled:bg-transparent' }, { color: 'primary', variant: 'link', class: 'text-(--ui-primary) hover:text-(--ui-primary)/75 disabled:text-(--ui-primary) aria-disabled:text-(--ui-primary) focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-primary)' }, { color: 'secondary', variant: 'link', class: 'text-(--ui-secondary) hover:text-(--ui-secondary)/75 disabled:text-(--ui-secondary) aria-disabled:text-(--ui-secondary) focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-secondary)' }, { color: 'success', variant: 'link', class: 'text-(--ui-success) hover:text-(--ui-success)/75 disabled:text-(--ui-success) aria-disabled:text-(--ui-success) focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-success)' }, { color: 'info', variant: 'link', class: 'text-(--ui-info) hover:text-(--ui-info)/75 disabled:text-(--ui-info) aria-disabled:text-(--ui-info) focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-info)' }, { color: 'warning', variant: 'link', class: 'text-(--ui-warning) hover:text-(--ui-warning)/75 disabled:text-(--ui-warning) aria-disabled:text-(--ui-warning) focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-warning)' }, { color: 'error', variant: 'link', class: 'text-(--ui-error) hover:text-(--ui-error)/75 disabled:text-(--ui-error) aria-disabled:text-(--ui-error) focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-(--ui-error)' }, { color: 'neutral', variant: 'solid', class: 'text-(--ui-bg) bg-(--ui-bg-inverted) hover:bg-(--ui-bg-inverted)/90 disabled:bg-(--ui-bg-inverted) aria-disabled:bg-(--ui-bg-inverted) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-border-inverted)' }, { color: 'neutral', variant: 'outline', class: 'ring ring-inset ring-(--ui-border-accented) text-(--ui-text) bg-(--ui-bg) hover:bg-(--ui-bg-elevated) disabled:bg-(--ui-bg) aria-disabled:bg-(--ui-bg) focus:outline-none focus-visible:ring-2 focus-visible:ring-(--ui-border-inverted)' }, { color: 'neutral', variant: 'soft', class: 'text-(--ui-text) bg-(--ui-bg-elevated) hover:bg-(--ui-bg-accented)/75 focus:outline-none focus-visible:bg-(--ui-bg-accented)/75 disabled:bg-(--ui-bg-elevated) aria-disabled:bg-(--ui-bg-elevated)' }, { color: 'neutral', variant: 'subtle', class: 'ring ring-inset ring-(--ui-border-accented) text-(--ui-text) bg-(--ui-bg-elevated) hover:bg-(--ui-bg-accented)/75 disabled:bg-(--ui-bg-elevated) aria-disabled:bg-(--ui-bg-elevated) focus:outline-none focus-visible:ring-2 focus-visible:ring-(--ui-border-inverted)' }, { color: 'neutral', variant: 'ghost', class: 'text-(--ui-text) hover:bg-(--ui-bg-elevated) focus:outline-none focus-visible:bg-(--ui-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-(--ui-text-muted) hover:text-(--ui-text) disabled:text-(--ui-text-muted) aria-disabled:text-(--ui-text-muted) focus:outline-none focus-visible:ring-inset focus-visible:ring-2 focus-visible:ring-(--ui-border-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' } } } }) ``` # ButtonGroup ## Usage Wrap multiple [Button](https://ui.nuxt.com/components/button) within a ButtonGroup to group them together. ```vue ``` ### Size Use the `size` prop to change the size of all the buttons. ```vue ``` ### Orientation Use the `orientation` prop to change the orientation of the buttons. Defaults to `horizontal`. ```vue ``` ## Examples ### With input You can use components like [Input](https://ui.nuxt.com/components/input), [InputMenu](https://ui.nuxt.com/components/input-menu), [Select](https://ui.nuxt.com/components/select) [SelectMenu](https://ui.nuxt.com/components/select-menu), etc. within a button group. ```vue ``` ### With tooltip You can use a [Tooltip](https://ui.nuxt.com/components/tooltip) within a button group. ```vue [ButtonGroupTooltipExample.vue] ``` ### With dropdown You can use a [DropdownMenu](https://ui.nuxt.com/components/dropdown-menu) within a button group. ```vue [ButtonGroupDropdownExample.vue] ``` ### With badge You can use a [Badge](https://ui.nuxt.com/components/badge) within a button group. ```vue [ButtonGroupBadgeExample.vue] ``` ## API ### Props ```ts /** * Props for the ButtonGroup component */ interface ButtonGroupProps { /** * The element or component this component should render as. */ as?: any; size?: "md" | "xs" | "sm" | "lg" | "xl" | undefined; /** * The orientation the buttons are laid out. * @default "\"horizontal\"" */ orientation?: "horizontal" | "vertical" | undefined; ui?: {} | undefined; } ``` ### Slots ```ts /** * Slots for the ButtonGroup component */ interface ButtonGroupSlots { default(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ ui: { buttonGroup: { base: 'relative', variants: { size: { xs: '', sm: '', md: '', lg: '', xl: '' }, orientation: { horizontal: 'inline-flex -space-x-px', vertical: 'flex flex-col -space-y-px' } } } } }) ``` # Calendar ::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. :: ## 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 ``` ### 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 ``` ### 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/components/chip) component to add events to specific days. ```vue [CalendarEventsExample.vue] ``` ### 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. :: ### As a DatePicker Use a [Button](https://ui.nuxt.com/components/button) and a [Popover](https://ui.nuxt.com/components/popover) component to create a date picker. ```vue [CalendarDatePickerExample.vue] ``` ### As a DateRangePicker Use a [Button](https://ui.nuxt.com/components/button) and a [Popover](https://ui.nuxt.com/components/popover) component to create a date range picker. ```vue [CalendarDateRangePickerExample.vue] ``` ## 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 | 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 | 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 | 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 | undefined; /** * Configure the prev month button. * `{ color: 'neutral', variant: 'ghost' }`{lang="ts-type"} */ prevMonth?: ButtonProps | undefined; color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | 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; ... 5 more ...; 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 day of the week to start the calendar on */ weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 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 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; } ``` ### 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: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', 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-(--ui-text-muted) data-unavailable:line-through data-unavailable:text-(--ui-text-muted) data-unavailable:pointer-events-none data-[selected]:text-(--ui-bg) data-today:font-semibold data-[outside-view]:text-(--ui-text-muted)', 'transition' ] }, variants: { color: { primary: { headCell: 'text-(--ui-primary)', cellTrigger: 'focus-visible:ring-(--ui-primary) data-[selected]:bg-(--ui-primary) data-today:not-data-[selected]:text-(--ui-primary) data-[highlighted]:bg-(--ui-primary)/20 hover:not-data-[selected]:bg-(--ui-primary)/20' }, secondary: { headCell: 'text-(--ui-secondary)', cellTrigger: 'focus-visible:ring-(--ui-secondary) data-[selected]:bg-(--ui-secondary) data-today:not-data-[selected]:text-(--ui-secondary) data-[highlighted]:bg-(--ui-secondary)/20 hover:not-data-[selected]:bg-(--ui-secondary)/20' }, success: { headCell: 'text-(--ui-success)', cellTrigger: 'focus-visible:ring-(--ui-success) data-[selected]:bg-(--ui-success) data-today:not-data-[selected]:text-(--ui-success) data-[highlighted]:bg-(--ui-success)/20 hover:not-data-[selected]:bg-(--ui-success)/20' }, info: { headCell: 'text-(--ui-info)', cellTrigger: 'focus-visible:ring-(--ui-info) data-[selected]:bg-(--ui-info) data-today:not-data-[selected]:text-(--ui-info) data-[highlighted]:bg-(--ui-info)/20 hover:not-data-[selected]:bg-(--ui-info)/20' }, warning: { headCell: 'text-(--ui-warning)', cellTrigger: 'focus-visible:ring-(--ui-warning) data-[selected]:bg-(--ui-warning) data-today:not-data-[selected]:text-(--ui-warning) data-[highlighted]:bg-(--ui-warning)/20 hover:not-data-[selected]:bg-(--ui-warning)/20' }, error: { headCell: 'text-(--ui-error)', cellTrigger: 'focus-visible:ring-(--ui-error) data-[selected]:bg-(--ui-error) data-today:not-data-[selected]:text-(--ui-error) data-[highlighted]:bg-(--ui-error)/20 hover:not-data-[selected]:bg-(--ui-error)/20' }, neutral: { headCell: 'text-(--ui-bg-inverted)', cellTrigger: 'focus-visible:ring-(--ui-border-inverted) data-[selected]:bg-(--ui-bg-inverted) data-today:not-data-[selected]:text-(--ui-bg-inverted) data-[highlighted]:bg-(--ui-bg-inverted)/20 hover:not-data-[selected]:bg-(--ui-bg-inverted)/10' } }, 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' } } }, defaultVariants: { size: 'md', color: 'primary' } } } }) ``` # Card ## Usage ```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', header: 'p-4 sm:px-6', body: 'p-4 sm:p-6', footer: 'p-4 sm:px-6' }, variants: { variant: { solid: { root: 'bg-(--ui-bg-inverted) text-(--ui-bg)' }, outline: { root: 'bg-(--ui-bg) ring ring-(--ui-border) divide-y divide-(--ui-border)' }, soft: { root: 'bg-(--ui-bg-elevated)/50 divide-y divide-(--ui-border)' }, subtle: { root: 'bg-(--ui-bg-elevated)/50 ring ring-(--ui-border) divide-y divide-(--ui-border)' } } }, defaultVariants: { variant: 'outline' } } } }) ``` # Carousel ## Usage ### Items Use the `items` prop as an array and render each item using the default slot: ::note Use your mouse to drag the carousel horizontally on desktop. :: ```vue [CarouselItemsExample.vue] ``` 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/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/components/icon). Defaults to `i-lucide-arrow-left` / `i-lucide-arrow-right`. ```vue [CarouselPrevNextIconExample.vue] ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/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/getting-started/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] ``` ## 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 | undefined; /** * Configure the next button when arrows are enabled. */ next?: ButtonProps | undefined; /** * The icon displayed in the next button. */ nextIcon?: string | 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?: AcceptableValue[] | 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?: any; ui?: { root?: ClassNameValue; viewport?: ClassNameValue; container?: ClassNameValue; item?: ClassNameValue; controls?: ClassNameValue; ... 4 more ...; 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; ... 13 more ...; watchFocus: FocusHandlerOptionType; }>>, "breakpoints">; } | undefined; } ``` ### Slots ```ts /** * Slots for the Carousel component */ interface CarouselSlots { default(): any; } ``` ### 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`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} | `Ref`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} | | `emblaApi`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} | [`Ref`{className="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-(--ui-border-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: 'bg-(--ui-border-inverted)' } } } } } }) ``` # 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 content: Hello! Tell me more about building AI chatbots with Nuxt UI Pro. side: right variant: soft --- ::: :: ::tip{to="https://ui.nuxt.com/components/chat-messages"} Use the [`ChatMessages`](https://ui.nuxt.com/components/chat-messages) component to display a list of chat messages. :: ### Content Use the `content` prop to display the message content. ```vue ``` ### Side Use the `side` prop to display the message on the left or right. ```vue ``` ::note When using the [`ChatMessages`](https://ui.nuxt.com/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/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/components/icon) component next to the message. ```vue ``` ### Avatar Use the `avatar` prop to display an [Avatar](https://ui.nuxt.com/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 { /** * Text content of the message. Use parts when possible. */ content: string; /** * A unique identifier for the message. */ id: string; /** * The 'data' role is deprecated. */ role: "data" | "system" | "user" | "assistant"; /** * @default "\"article\"" */ as?: any; icon?: string | undefined; ui?: { root?: ClassNameValue; container?: ClassNameValue; leading?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; content?: ClassNameValue; actions?: ClassNameValue; } | undefined; avatar?: (AvatarProps & { [key: string]: any; }) | undefined; variant?: "solid" | "outline" | "soft" | "subtle" | "naked" | undefined; side?: "right" | "left" | undefined; /** * For data messages. */ data?: JSONValue | 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: Message) => 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; /** * The timestamp of the message. */ createdAt?: Date | undefined; /** * Reasoning for the message. */ reasoning?: string | undefined; /** * Additional attachments to be sent along with the message. */ experimental_attachments?: Attachment[] | undefined; /** * Additional message-specific information added on the server via StreamData */ annotations?: JSONValue[] | undefined; /** * Tool invocations (that can be tool calls or tool results, depending on whether or not the invocation has finished) * that the assistant made as part of this message. */ toolInvocations?: ToolInvocation[] | undefined; /** * The parts of the message. Use this for rendering the message in the UI. * * Assistant messages can have text, reasoning and tool invocation parts. * User messages can have text parts. */ parts?: (TextUIPart | ReasoningUIPart | ToolInvocationUIPart | SourceUIPart | FileUIPart | StepStartUIPart)[] | undefined; } ``` ### Slots ```ts /** * Slots for the ChatMessage component */ interface ChatMessageSlots { leading(): any; content(): any; actions(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { chatMessage: { slots: { root: 'group/message relative w-full', container: 'relative flex items-start group-data-[role=user]/message:max-w-[75%]', leading: 'inline-flex items-center justify-center min-h-6', leadingIcon: 'shrink-0', leadingAvatar: 'shrink-0', leadingAvatarSize: '', content: 'relative text-pretty', actions: [ 'opacity-0 group-hover/message:opacity-100 absolute bottom-0 flex items-center', 'transition-opacity' ] }, variants: { variant: { solid: { content: 'bg-(--ui-bg-inverted) text-(--ui-bg)' }, outline: { content: 'bg-(--ui-bg) ring ring-(--ui-border)' }, soft: { content: 'bg-(--ui-bg-elevated)/50' }, subtle: { content: 'bg-(--ui-bg-elevated)/50 ring ring-(--ui-border)' }, naked: { content: '' } }, side: { left: { container: 'rtl:justify-end' }, right: { container: 'ltr:justify-end ms-auto' } }, 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-[calc(var(--ui-radius)*2)] min-h-12', leading: 'mt-2' } }, { variant: [ 'solid', 'outline', 'soft', 'subtle' ], compact: true, class: { content: 'px-2 py-1 rounded-[calc(var(--ui-radius)*2)] min-h-8', leading: 'mt-1' } } ], defaultVariants: { variant: 'naked' } } } }) ``` # ChatMessages ## Usage The ChatMessages component displays a list of [ChatMessage](https://ui.nuxt.com/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 sent by the `useChat` composable: - `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/components/chat-message) props for `user` messages. Defaults to: - `side: 'right'`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `variant: 'soft'`{className="language-ts-type 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/components/chat-message) props for `assistant` messages. Defaults to: - `side: 'left'`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `variant: 'naked'`{className="language-ts-type 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'`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `variant: 'outline'`{className="language-ts-type 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/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/components/icon). Defaults to `i-lucide-arrow-down`. ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/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/getting-started/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://sdk.vercel.ai/docs/getting-started/nuxt"} These chat components are designed to be used with the `useChat` composable from **Vercel AI SDK**. :: ::callout --- icon: i-simple-icons-github target: _blank to: https://github.com/nuxt-ui-pro/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 `useChat` composable 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] {4,11-15} ``` ::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. As Nuxt UI Pro provides pre-styled prose components, your content will be automatically styled. :: ## API ### Props ```ts /** * Props for the ChatMessages component */ interface ChatMessagesProps { /** * @default "true" */ autoScroll?: boolean | Partial | undefined; /** * @default "false" */ shouldAutoScroll?: boolean | undefined; /** * @default "true" */ shouldScrollToBottom?: boolean | undefined; /** * @default "0" */ spacingOffset?: number | undefined; ui?: { root?: ClassNameValue; indicator?: ClassNameValue; viewport?: ClassNameValue; autoScroll?: ClassNameValue; } | undefined; messages?: Message[] | undefined; /** * Render the messages in a compact style. * This is done automatically when used inside a `UChatPalette`{lang="ts-type"}. */ compact?: boolean | 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; status?: "error" | "submitted" | "streaming" | "ready" | undefined; /** * The icon displayed in the auto scroll button. */ autoScrollIcon?: string | undefined; } ``` ### Slots ```ts /** * Slots for the ChatMessages component */ interface ChatMessagesSlots { default(): any; indicator(): any; viewport(): any; content(): any; leading(): any; actions(): any; } ``` ::tip You can use all the slots of the [`ChatMessage`](https://ui.nuxt.com/components/chat-message#slots) component inside ChatMessages, they are automatically forwarded allowing you to customize individual messages when using the `messages` prop. ```vue {3-5} ``` :: ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { 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-(--ui-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: '' } } } } }) ``` # ChatPalette ## Usage The ChatPalette component is a structured layout wrapper that organizes [ChatMessages](https://ui.nuxt.com/components/chat-messages) in a scrollable content area and [ChatPrompt](https://ui.nuxt.com/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://sdk.vercel.ai/docs/getting-started/nuxt"} These chat components are designed to be used with the `useChat` composable from **Vercel AI SDK**. :: ### Within a Modal You can use the ChatPalette component inside a [Modal](https://ui.nuxt.com/components/modal)'s content. ```vue [ChatPaletteModalExample.vue] ``` ### Within ContentSearch You can use the ChatPalette component conditionally inside [ContentSearch](https://ui.nuxt.com/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({ uiPro: { chatPalette: { slots: { root: 'relative flex-1 flex flex-col min-h-0 min-w-0', prompt: 'px-0 rounded-t-none border-t border-(--ui-border)', close: '', content: 'overflow-y-auto flex-1 flex flex-col py-3' } } } }) ``` # ChatPrompt ## Usage The ChatPrompt component renders a `
` element and extends the [Textarea](https://ui.nuxt.com/components/textarea) component so you can pass any property such as `icon`, `placeholder`, `autofocus`, etc. ::code-preview :::u-chat-prompt --- variant: subtle --- ::::u-chat-prompt-submit{className="rounded-full" color="neutral"} :::: #footer ::::u-select --- items: - label: Gemini 2.5 Pro value: gemini-2.5-pro icon: i-simple-icons-googlegemini - label: GPT-4o value: gpt-4o icon: i-simple-icons-openai - label: Claude 3.5 Sonnet value: claude-3.5-sonnet icon: i-simple-icons-anthropic - label: Llama 4 value: llama-4 icon: i-simple-icons-ollama icon: i-simple-icons-openai modelValue: gpt-4o placeholder: Select a model variant: ghost --- :::: ::: :: ::note The ChatPrompt handles the following events: - The form is submitted when the user presses `` or when the user clicks on the submit button. - The textarea is blurred when `` is pressed and emits a `close` event. :: ### Variant Use the `variant` prop to change the style of the prompt. Defaults to `outline`. ```vue ``` ## Examples ::note{target="_blank" to="https://sdk.vercel.ai/docs/getting-started/nuxt"} These chat components are designed to be used with the `useChat` composable from **Vercel AI SDK**. :: ::callout --- icon: i-simple-icons-github target: _blank to: https://github.com/nuxt-ui-pro/chat --- Check out the source code of our **AI Chat template** on GitHub for a real-life example. :: ### Within a page Use the ChatPrompt component with the `useChat` composable to display a chat prompt within a page. Pass the `input` prop alongside the `error` prop to disable the textarea when an error occurs. ```vue [pages/[id\\].vue] {4,21,23} ``` You can also use it as a starting point for a chat interface. ```vue [pages/index.vue] {23,25} ``` ## API ### Props ```ts /** * Props for the ChatPrompt component */ interface ChatPromptProps { /** * @default "\"form\"" */ as?: any; /** * @default "true" */ autofocus?: boolean | undefined; /** * @default "true" */ autoresize?: boolean | undefined; /** * @default "1" */ rows?: number | undefined; /** * Display an icon based on the `leading` and `trailing` props. */ icon?: string | undefined; error?: Error | undefined; modelValue?: string | undefined; ui?: ({ root?: ClassNameValue; header?: ClassNameValue; body?: ClassNameValue; footer?: ClassNameValue; base?: ClassNameValue; } & { ...; }) | undefined; /** * When `true`, the loading icon will be displayed. */ loading?: boolean | undefined; /** * The icon when the `loading` prop is `true`. */ loadingIcon?: string | undefined; /** * Display an avatar on the left side. */ avatar?: AvatarProps | undefined; variant?: "outline" | "soft" | "subtle" | "naked" | undefined; /** * The placeholder text for the textarea. */ placeholder?: string | undefined; autofocusDelay?: number | undefined; autoresizeDelay?: number | undefined; maxrows?: number | undefined; } ``` ### Slots ```ts /** * Slots for the ChatPrompt component */ interface ChatPromptSlots { header(): any; footer(): any; leading(): any; default(): any; trailing(): any; } ``` ### Emits No events available for this component. ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { chatPrompt: { slots: { root: 'relative flex flex-col items-stretch gap-2 px-2.5 py-2 w-full rounded-[calc(var(--ui-radius)*2)] backdrop-blur', header: 'flex items-center gap-1.5', body: 'items-start', footer: 'flex items-center justify-between gap-1.5', base: 'text-base/5' }, variants: { variant: { outline: { root: 'bg-(--ui-bg)/75 ring ring-(--ui-border)' }, soft: { root: 'bg-(--ui-bg-elevated)/50' }, subtle: { root: 'bg-(--ui-bg-elevated)/50 ring ring-(--ui-border)' }, naked: { root: '' } } }, defaultVariants: { variant: 'outline' } } } }) ``` # ChatPromptSubmit ## Usage The ChatPromptSubmit component is used inside the [ChatPrompt](https://ui.nuxt.com/components/chat-prompt) component to submit the prompt. It automatically handles the different `status` values to control the chat. It extends the [Button](https://ui.nuxt.com/components/button) component, so you can pass any property such as `color`, `variant`, `size`, etc. ::code-preview :::u-chat-prompt-submit ::: #code ```vue ``` :: ::note You can also use it inside the `footer` slot of the [`ChatPrompt`](https://ui.nuxt.com/components/chat-prompt) component. :: ### Ready When its status is `ready`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}, use the `color`, `variant` and `icon` props to customize the Button. Defaults to: - `color="primary"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `variant="solid"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `icon="i-lucide-arrow-up"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/icons/nuxt#theme"} You can customize this icon globally in your `app.config.ts` under `ui.icons.arrowUp` key. ::: #vue :::tip{to="https://ui.nuxt.com/getting-started/icons/vue#theme"} You can customize this icon globally in your `vite.config.ts` under `ui.icons.arrowUp` key. ::: :: ### Submitted When its status is `submitted`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}, use the `submitted-color`, `submitted-variant` and `submitted-icon` props to customize the Button. Defaults to: - `submittedColor="neutral"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `submittedVariant="subtle"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `submittedIcon="i-lucide-square"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} ::note The `stop` event is emitted when the user clicks on the Button. :: ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/icons/nuxt#theme"} You can customize this icon globally in your `app.config.ts` under `ui.icons.stop` key. ::: #vue :::tip{to="https://ui.nuxt.com/getting-started/icons/vue#theme"} You can customize this icon globally in your `vite.config.ts` under `ui.icons.stop` key. ::: :: ### Streaming When its status is `streaming`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}, use the `streaming-color`, `streaming-variant` and `streaming-icon` props to customize the Button. Defaults to: - `streamingColor="neutral"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `streamingVariant="subtle"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `streamingIcon="i-lucide-square"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} ::note The `stop` event is emitted when the user clicks on the Button. :: ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/icons/nuxt#theme"} You can customize this icon globally in your `app.config.ts` under `ui.icons.stop` key. ::: #vue :::tip{to="https://ui.nuxt.com/getting-started/icons/vue#theme"} You can customize this icon globally in your `vite.config.ts` under `ui.icons.stop` key. ::: :: ### Error When its status is `error`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}, use the `error-color`, `error-variant` and `error-icon` props to customize the Button. Defaults to: - `errorColor="error"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `errorVariant="soft"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `errorIcon="i-lucide-rotate-ccw"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} ::note The `reload` event is emitted when the user clicks on the Button. :: ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/icons/nuxt#theme"} You can customize this icon globally in your `app.config.ts` under `ui.icons.reload` key. ::: #vue :::tip{to="https://ui.nuxt.com/getting-started/icons/vue#theme"} You can customize this icon globally in your `vite.config.ts` under `ui.icons.reload` key. ::: :: ## Examples ::note{target="_blank" to="https://sdk.vercel.ai/docs/getting-started/nuxt"} These chat components are designed to be used with the `useChat` composable from **Vercel AI SDK**. :: ::callout --- icon: i-simple-icons-github target: _blank to: https://github.com/nuxt-ui-pro/chat --- Check out the source code of our **AI Chat template** on GitHub for a real-life example. :: ### Within a page Use the ChatPromptSubmit component with the `useChat` composable to display a chat prompt within a page. Pass the `status` prop and listen to the `stop` and `reload` events to control the chat. ```vue [pages/[id\\].vue] {4,22} ``` ## API ### Props ```ts /** * Props for the ChatPromptSubmit component */ interface ChatPromptSubmitProps { } ``` ### Slots ```ts /** * Slots for the ChatPromptSubmit component */ interface ChatPromptSubmitSlots { } ``` ### Emits No events available for this component. ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { chatPromptSubmit: { slots: { base: '' } } } }) ``` # Checkbox ## Usage Use the `v-model` directive to control the checked state of the Checkbox. ```vue ``` Use the `default-value` prop to set the initial value when you do not need to control its state. ```vue ``` ### Indeterminate Use the `indeterminate` value in the `v-model` directive or `default-value` prop to set the Checkbox to an [indeterminate state](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#indeterminate_state_checkboxes){rel="nofollow"}. ```vue ``` ### Indeterminate Icon Use the `indeterminate-icon` prop to customize the indeterminate icon. Defaults to `i-lucide-minus`. ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/icons/nuxt#theme"} You can customize this icon globally in your `app.config.ts` under `ui.icons.minus` key. ::: #vue :::tip{to="https://ui.nuxt.com/getting-started/icons/vue#theme"} You can customize this icon globally in your `vite.config.ts` under `ui.icons.minus` key. ::: :: ### Label Use the `label` prop to set the label of the Checkbox. ```vue ``` When using the `required` prop, an asterisk is added next to the label. ```vue ``` ### Description Use the `description` prop to set the description of the Checkbox. ```vue ``` ### Icon Use the `icon` prop to set the icon of the Checkbox when it is checked. Defaults to `i-lucide-check`. ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/icons/nuxt#theme"} You can customize this icon globally in your `app.config.ts` under `ui.icons.check` key. ::: #vue :::tip{to="https://ui.nuxt.com/getting-started/icons/vue#theme"} You can customize this icon globally in your `vite.config.ts` under `ui.icons.check` key. ::: :: ### Color Use the `color` prop to change the color of the Checkbox. ```vue ``` ### Size Use the `size` prop to change the size of the Checkbox. ```vue ``` ### Disabled Use the `disabled` prop to disable the Checkbox. ```vue ``` ## API ### Props ```ts /** * Props for the Checkbox component */ interface CheckboxProps { /** * The element or component this component should render as. */ as?: any; label?: string | undefined; description?: string | undefined; color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined; size?: "xs" | "sm" | "md" | "lg" | "xl" | undefined; /** * The icon displayed when checked. */ icon?: string | undefined; /** * The icon displayed when the checkbox is indeterminate. */ indeterminateIcon?: string | undefined; ui?: { root?: ClassNameValue; base?: ClassNameValue; container?: ClassNameValue; wrapper?: ClassNameValue; icon?: ClassNameValue; label?: ClassNameValue; description?: ClassNameValue; } | undefined; /** * When `true`, prevents the user from interacting with the checkbox */ disabled?: boolean | undefined; /** * The value given as data when submitted with a `name`. */ value?: AcceptableValue | undefined; /** * The name of the field. Submitted with its owning form as part of a name/value pair. */ name?: string | undefined; /** * When `true`, indicates that the user must set the value before the owning form can be submitted. */ required?: boolean | undefined; /** * Id of the element */ id?: string | undefined; /** * The value of the checkbox when it is initially rendered. Use when you do not need to control its value. */ defaultValue?: boolean | "indeterminate" | undefined; /** * @default "undefined" */ modelValue?: boolean | "indeterminate" | undefined; } ``` ### Slots ```ts /** * Slots for the Checkbox component */ interface CheckboxSlots { label(): any; description(): any; } ``` ### Emits ```ts /** * Emitted events for the Checkbox component */ interface CheckboxEmits { change: (payload: [payload: Event]) => void; update:modelValue: (payload: [value: boolean | "indeterminate"]) => void; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ ui: { checkbox: { slots: { root: 'relative flex items-start', base: 'shrink-0 flex items-center justify-center rounded-sm text-(--ui-bg) ring ring-inset ring-(--ui-border-accented) focus-visible:outline-2 focus-visible:outline-offset-2', container: 'flex items-center', wrapper: 'ms-2', icon: 'shrink-0 size-full', label: 'block font-medium text-(--ui-text)', description: 'text-(--ui-text-muted)' }, variants: { color: { primary: 'focus-visible:outline-(--ui-primary)', secondary: 'focus-visible:outline-(--ui-secondary)', success: 'focus-visible:outline-(--ui-success)', info: 'focus-visible:outline-(--ui-info)', warning: 'focus-visible:outline-(--ui-warning)', error: 'focus-visible:outline-(--ui-error)', neutral: 'focus-visible:outline-(--ui-border-inverted)' }, size: { xs: { base: 'size-3', container: 'h-4', wrapper: 'text-xs' }, sm: { base: 'size-3.5', container: 'h-4', wrapper: 'text-xs' }, md: { base: 'size-4', container: 'h-5', wrapper: 'text-sm' }, lg: { base: 'size-4.5', container: 'h-5', wrapper: 'text-sm' }, xl: { base: 'size-5', container: 'h-6', wrapper: 'text-base' } }, required: { true: { label: "after:content-['*'] after:ms-0.5 after:text-(--ui-error)" } }, disabled: { true: { base: 'cursor-not-allowed opacity-75', label: 'cursor-not-allowed opacity-75', description: 'cursor-not-allowed opacity-75' } }, checked: { true: '' } }, compoundVariants: [ { color: 'primary', checked: true, class: 'ring-2 ring-(--ui-primary) bg-(--ui-primary)' }, { color: 'secondary', checked: true, class: 'ring-2 ring-(--ui-secondary) bg-(--ui-secondary)' }, { color: 'success', checked: true, class: 'ring-2 ring-(--ui-success) bg-(--ui-success)' }, { color: 'info', checked: true, class: 'ring-2 ring-(--ui-info) bg-(--ui-info)' }, { color: 'warning', checked: true, class: 'ring-2 ring-(--ui-warning) bg-(--ui-warning)' }, { color: 'error', checked: true, class: 'ring-2 ring-(--ui-error) bg-(--ui-error)' }, { color: 'neutral', checked: true, class: 'ring-2 ring-(--ui-border-inverted) bg-(--ui-bg-inverted)' } ], defaultVariants: { size: 'md', color: 'primary' } } } }) ``` # Chip ## Usage Wrap any component with a Chip to display an indicator. ```vue ``` ### Color Use the `color` prop to change the color of the Chip. ```vue ``` ### Size Use the `size` prop to change the size of the Chip. ```vue ``` ### Text Use the `text` prop to set the text of the Chip. ```vue ``` ### Position Use the `position` prop to change the position of the Chip. ```vue ``` ### Inset Use the `inset` prop to display the Chip inside the component. This is useful when dealing with rounded components. ```vue ``` ### Standalone Use the `standalone` prop alongside the `inset` prop to display the Chip inline. ```vue ``` ::note It's used this way in the [`CommandPalette`](https://ui.nuxt.com/components/command-palette), [`InputMenu`](https://ui.nuxt.com/components/input-menu), [`Select`](https://ui.nuxt.com/components/select) or [`SelectMenu`](https://ui.nuxt.com/components/select-menu) components for example. :: ## Examples ### Control visibility You can control the visibility of the Chip using the `show` prop. ```vue [ChipShowExample.vue] ``` ::note In this example, the Chip has a color per status and is displayed when the status is not `offline`. :: ## API ### Props ```ts /** * Props for the Chip component */ interface ChipProps { /** * The element or component this component should render as. */ as?: any; /** * Display some text inside the chip. */ text?: string | number | undefined; color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined; size?: "3xs" | "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | undefined; /** * The position of the chip. */ position?: "top-right" | "bottom-right" | "top-left" | "bottom-left" | undefined; /** * When `true`, keep the chip inside the component for rounded elements. * @default "false" */ inset?: boolean | undefined; /** * When `true`, render the chip relatively to the parent. * @default "false" */ standalone?: boolean | undefined; ui?: { root?: ClassNameValue; base?: ClassNameValue; } | undefined; /** * @default "true" */ show?: boolean | undefined; } ``` ### Slots ```ts /** * Slots for the Chip component */ interface ChipSlots { default(): any; content(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ ui: { chip: { slots: { root: 'relative inline-flex items-center justify-center shrink-0', base: 'rounded-full ring ring-(--ui-bg) flex items-center justify-center text-(--ui-bg) font-medium whitespace-nowrap' }, variants: { color: { primary: 'bg-(--ui-primary)', secondary: 'bg-(--ui-secondary)', success: 'bg-(--ui-success)', info: 'bg-(--ui-info)', warning: 'bg-(--ui-warning)', error: 'bg-(--ui-error)', neutral: 'bg-(--ui-text-muted)' }, size: { '3xs': 'h-[4px] min-w-[4px] text-[4px]', '2xs': 'h-[5px] min-w-[5px] text-[5px]', xs: 'h-[6px] min-w-[6px] text-[6px]', sm: 'h-[7px] min-w-[7px] text-[7px]', md: 'h-[8px] min-w-[8px] text-[8px]', lg: 'h-[9px] min-w-[9px] text-[9px]', xl: 'h-[10px] min-w-[10px] text-[10px]', '2xl': 'h-[11px] min-w-[11px] text-[11px]', '3xl': 'h-[12px] min-w-[12px] text-[12px]' }, position: { 'top-right': 'top-0 right-0', 'bottom-right': 'bottom-0 right-0', 'top-left': 'top-0 left-0', 'bottom-left': 'bottom-0 left-0' }, inset: { false: '' }, standalone: { false: 'absolute' } }, compoundVariants: [ { position: 'top-right', inset: false, class: '-translate-y-1/2 translate-x-1/2 transform' }, { position: 'bottom-right', inset: false, class: 'translate-y-1/2 translate-x-1/2 transform' }, { position: 'top-left', inset: false, class: '-translate-y-1/2 -translate-x-1/2 transform' }, { position: 'bottom-left', inset: false, class: 'translate-y-1/2 -translate-x-1/2 transform' } ], defaultVariants: { size: 'md', color: 'primary', position: 'top-right' } } } }) ``` # Collapsible ## Usage Use a [Button](https://ui.nuxt.com/components/button) or any other component in the default slot of the Collapsible. Then, use the `#content` slot to add the content displayed when the Collapsible is open. ```vue ``` ### Unmount Use the `unmount-on-hide` prop to prevent the content from being unmounted when the Collapsible is collapsed. Defaults to `true`. ```vue ``` ::note You can inspect the DOM to see the content being rendered. :: ### Disabled Use the `disabled` prop to disable the Collapsible. ```vue ``` ## Examples ### Control open state You can control the open state by using the `default-open` prop or the `v-model:open` directive. ```vue [CollapsibleOpenExample.vue] ``` ::note In this example, leveraging [`defineShortcuts`](https://ui.nuxt.com/composables/define-shortcuts), you can toggle the Collapsible by pressing ``. :: ::tip This allows you to move the trigger outside of the Collapsible or remove it entirely. :: ### With rotating icon Here is an example with a rotating icon in the Button that indicates the open state of the Collapsible. ```vue [CollapsibleIconExample.vue] ``` ## API ### Props ```ts /** * Props for the Collapsible component */ interface CollapsibleProps { /** * The element or component this component should render as. */ as?: any; ui?: { root?: ClassNameValue; content?: ClassNameValue; } | undefined; /** * When `true`, prevents the user from interacting with the collapsible. */ disabled?: boolean | undefined; /** * The open state of the collapsible when it is initially rendered.
Use when you do not need to control its open state. */ defaultOpen?: boolean | undefined; /** * The controlled open state of the collapsible. Can be binded with `v-model`. */ open?: boolean | undefined; /** * When `true`, the element will be unmounted on closed state. * @default "true" */ unmountOnHide?: boolean | undefined; } ``` ### Slots ```ts /** * Slots for the Collapsible component */ interface CollapsibleSlots { default(): any; content(): any; } ``` ### Emits ```ts /** * Emitted events for the Collapsible component */ interface CollapsibleEmits { update:open: (payload: [value: boolean]) => void; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ ui: { collapsible: { slots: { root: '', content: 'data-[state=open]:animate-[collapsible-down_200ms_ease-out] data-[state=closed]:animate-[collapsible-up_200ms_ease-out] overflow-hidden' } } } }) ``` # ColorModeAvatar ## Usage The ColorModeAvatar component extends the [Avatar](https://ui.nuxt.com/components/avatar) component, so you can pass any property such as `size`, `icon`, etc. Use the `light` and `dark` props to define the source for light and dark mode. ```vue ``` ::note Switch between light and dark mode to see the different images: :u-color-mode-select{size="sm"} :: ## API ### Props ```ts /** * Props for the ColorModeAvatar component */ interface ColorModeAvatarProps { light: string; dark: string; icon?: string | undefined; /** * The element or component this component should render as. */ as?: any; size?: "md" | "xs" | "sm" | "lg" | "xl" | "3xs" | "2xs" | "2xl" | "3xl" | undefined; ui?: { root?: ClassNameValue; image?: ClassNameValue; fallback?: ClassNameValue; icon?: ClassNameValue; } | undefined; alt?: string | undefined; text?: string | undefined; } ``` # ColorModeButton ## Usage The ColorModeButton component extends the [Button](https://ui.nuxt.com/components/button) component, so you can pass any property such as `color`, `variant`, `size`, etc. ```vue ``` ::note The button defaults to `color="neutral"` and `variant="ghost"`. :: ## Examples ### With custom icons ::framework-only #nuxt :::div Use the `app.config.ts` to customize the icon with the `ui.icons` property: ```ts [app.config.ts] export default defineAppConfig({ ui: { icons: { light: 'i-ph-sun', dark: 'i-ph-moon' } } }) ``` ::: #vue :::div Use the `vite.config.ts` to customize the icon with the `ui.icons` property: ```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: { icons: { light: 'i-ph-sun', dark: 'i-ph-moon' } } }) ] }) ``` ::: :: ### With fallback slot As the button is wrapped in a [ClientOnly](https://nuxt.com/docs/api/components/client-only){rel="nofollow"} component, you can pass a `fallback` slot to display a placeholder while the component is loading. ```vue ``` ## API ### Props ```ts /** * Props for the ColorModeButton component */ interface ColorModeButtonProps { /** * @default "\"neutral\"" */ color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined; /** * @default "\"ghost\"" */ variant?: "link" | "solid" | "outline" | "soft" | "subtle" | "ghost" | undefined; disabled?: boolean | undefined; /** * The element or component this component should render as when not a link. */ as?: any; size?: "md" | "xs" | "sm" | "lg" | "xl" | undefined; ui?: { base?: ClassNameValue; label?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; trailingIcon?: ClassNameValue; } | undefined; } ``` ### Slots ```ts /** * Slots for the ColorModeButton component */ interface ColorModeButtonSlots { fallback(): any; } ``` # ColorModeImage ## Usage The ColorModeImage component uses the `` component when [`@nuxt/image`](https://github.com/nuxt/image){rel="nofollow"} is installed, falling back to `img` otherwise. ```vue ``` ::note Switch between light and dark mode to see the different images: :u-color-mode-select{size="sm"} :: ## API ### Props ```ts /** * Props for the ColorModeImage component */ interface ColorModeImageProps { light: string; dark: string; } ``` # ColorModeSelect ## Usage The ColorModeSelect component extends the [SelectMenu](https://ui.nuxt.com/components/select-menu) component, so you can pass any property such as `color`, `variant`, `size`, etc. ```vue ``` ## Examples ### With custom icons ::framework-only #nuxt :::div Use the `app.config.ts` to customize the icon with the `ui.icons` property: ```ts [app.config.ts] export default defineAppConfig({ ui: { icons: { system: 'i-ph-desktop', light: 'i-ph-sun', dark: 'i-ph-moon' } } }) ``` ::: #vue :::div Use the `vite.config.ts` to customize the icon with the `ui.icons` property: ```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: { icons: { light: 'i-ph-sun', dark: 'i-ph-moon' } } }) ] }) ``` ::: :: ## API ### Props ```ts /** * Props for the ColorModeSelect component */ interface ColorModeSelectProps { /** * The icon displayed to open the menu. */ trailingIcon?: string | undefined; /** * When `true`, prevents the user from interacting with listbox */ disabled?: boolean | undefined; color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined; size?: "md" | "xs" | "sm" | "lg" | "xl" | undefined; ui?: { base?: ClassNameValue; leading?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; ... 22 more ...; focusScope?: ClassNameValue; } | undefined; /** * The content of the menu. */ content?: (Omit & Partial>) | undefined; variant?: "outline" | "soft" | "subtle" | "ghost" | "none" | undefined; /** * The icon displayed when an item is selected. */ selectedIcon?: string | undefined; /** * Display an arrow alongside the menu. */ arrow?: boolean | Omit | undefined; /** * Render the menu in a portal. */ portal?: boolean | undefined; } ``` # ColorModeSwitch ## Usage The ColorModeSwitch component extends the [Switch](https://ui.nuxt.com/components/switch) component, so you can pass any property such as `color`, `size`, etc. ```vue ``` ## Examples ### With custom icons ::framework-only #nuxt :::div Use the `app.config.ts` to customize the icon with the `ui.icons` property: ```ts [app.config.ts] export default defineAppConfig({ ui: { icons: { light: 'i-ph-sun', dark: 'i-ph-moon' } } }) ``` ::: #vue :::div Use the `vite.config.ts` to customize the icon with the `ui.icons` property: ```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: { icons: { light: 'i-ph-sun', dark: 'i-ph-moon' } } }) ] }) ``` ::: :: ## API ### Props ```ts /** * Props for the ColorModeSwitch component */ interface ColorModeSwitchProps { /** * When `true`, prevents the user from interacting with the switch. */ disabled?: boolean | undefined; /** * The element or component this component should render as. */ as?: any; color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined; size?: "md" | "xs" | "sm" | "lg" | "xl" | undefined; ui?: { root?: ClassNameValue; base?: ClassNameValue; container?: ClassNameValue; thumb?: ClassNameValue; icon?: ClassNameValue; wrapper?: ClassNameValue; label?: ClassNameValue; description?: ClassNameValue; } | undefined; } ``` # ColorPicker ## Usage Use the `v-model` directive to control the value of the ColorPicker. ```vue ``` Use the `default-value` prop to set the initial value when you do not need to control its state. ```vue ``` ### RGB Format Use the `format` prop to set `rgb` value of the ColorPicker. ```vue ``` ### HSL Format Use the `format` prop to set `hsl` value of the ColorPicker. ```vue ``` ### CMYK Format Use the `format` prop to set `cmyk` value of the ColorPicker. ```vue ``` ### CIELab Format Use the `format` prop to set `lab` value of the ColorPicker. ```vue ``` ### Throttle Use the `throttle` prop to set the throttle value of the ColorPicker. ```vue ``` ### Size Use the `size` prop to set the size of the ColorPicker. ```vue ``` ### Disabled Use the `disabled` prop to disable the ColorPicker. ```vue ``` ## Examples ### As a Color chooser Use a [Button](https://ui.nuxt.com/components/button) and a [Popover](https://ui.nuxt.com/components/popover) component to create a color chooser. ```vue [ColorPickerChooserExample.vue] ``` ## API ### Props ```ts /** * Props for the ColorPicker component */ interface ColorPickerProps { /** * The element or component this component should render as. */ as?: any; /** * Throttle time in ms for the color picker * @default "50" */ throttle?: number | undefined; /** * Disable the color picker */ disabled?: boolean | undefined; /** * The default value of the color picker * @default "\"#FFFFFF\"" */ defaultValue?: string | undefined; /** * Format of the color * @default "\"hex\"" */ format?: "hex" | "rgb" | "hsl" | "cmyk" | "lab" | undefined; size?: "xs" | "sm" | "md" | "lg" | "xl" | undefined; ui?: { root?: ClassNameValue; picker?: ClassNameValue; selector?: ClassNameValue; selectorBackground?: ClassNameValue; selectorThumb?: ClassNameValue; track?: ClassNameValue; trackThumb?: ClassNameValue; } | undefined; modelValue?: string | undefined; } ``` ### Emits ```ts /** * Emitted events for the ColorPicker component */ interface ColorPickerEmits { update:modelValue: (payload: [value: string | undefined]) => void; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ ui: { colorPicker: { slots: { root: 'data-[disabled]:opacity-75', picker: 'flex gap-4', selector: 'rounded-md', selectorBackground: 'w-full h-full relative rounded-md', selectorThumb: '-translate-y-1/2 -translate-x-1/2 absolute size-4 ring-2 ring-(--color-white) rounded-full cursor-pointer data-[disabled]:cursor-not-allowed', track: 'w-[8px] relative rounded-md', trackThumb: 'absolute transform -translate-y-1/2 -translate-x-[4px] rtl:translate-x-[4px] size-4 rounded-full ring-2 ring-(--color-white) cursor-pointer data-[disabled]:cursor-not-allowed' }, variants: { size: { xs: { selector: 'w-38 h-38', track: 'h-38' }, sm: { selector: 'w-40 h-40', track: 'h-40' }, md: { selector: 'w-42 h-42', track: 'h-42' }, lg: { selector: 'w-44 h-44', track: 'h-44' }, xl: { selector: 'w-46 h-46', track: 'h-46' } } }, compoundVariants: [], defaultVariants: { size: 'md' } } } }) ``` # CommandPalette ## Usage Use the `v-model` directive to control the value of the CommandPalette or the `default-value` prop to set the initial value when you do not need to control its state. ::tip{to="https://ui.nuxt.com/#control-selected-items"} You can also use the `@update:model-value` event to listen to the selected item(s). :: ### Groups The CommandPalette component filters groups and ranks matching commands by relevance as users type. It provides dynamic, instant search results for efficient command discovery. Use the `groups` prop as an array of objects with the following properties: - `id: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `label?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `slot?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `items?: CommandPaletteItem[]`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - [`ignoreFilter?: boolean`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-ignore-filter) - [`postFilter?: (searchTerm: string, items: T[]) => T[]`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-post-filtered-items) - `highlightedIcon?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} ::caution You must provide an `id` for each group otherwise the group will be ignored. :: Each group contains an `items` array of objects that define the commands. Each item can have the following properties: - `prefix?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `label?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `suffix?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `icon?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `avatar?: AvatarProps`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `chip?: ChipProps`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `kbds?: string[] | KbdProps[]`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `active?: boolean`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `loading?: boolean`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `disabled?: boolean`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - [`slot?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-custom-slot) - `onSelect?(e?: Event): void`{className="language-ts-type 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/components/link#props) component such as `to`, `target`, etc. ```vue ``` ### Multiple Use the `multiple` prop to allow multiple selections. ```vue ``` ::caution Ensure to pass an array to the `default-value` prop or the `v-model` directive. :: ### Placeholder Use the `placeholder` prop to change the placeholder text. ```vue ``` ### Icon Use the `icon` prop to customize the input [Icon](https://ui.nuxt.com/components/icon). Defaults to `i-lucide-search`. ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/icons/nuxt#theme"} You can customize this icon globally in your `app.config.ts` under `ui.icons.search` key. ::: #vue :::tip{to="https://ui.nuxt.com/getting-started/icons/vue#theme"} You can customize this icon globally in your `vite.config.ts` under `ui.icons.search` key. ::: :: ### Loading Use the `loading` prop to show a loading icon on the CommandPalette. ```vue ``` ### Loading Icon Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide-refresh-cw`. ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/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/getting-started/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 CommandPalette. ```vue ``` ### Close Use the `close` prop to display a [Button](https://ui.nuxt.com/components/button) to dismiss the CommandPalette. ::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/components/button) component to customize it. ```vue ``` ### Close Icon Use the `close-icon` prop to customize the close button [Icon](https://ui.nuxt.com/components/icon). Defaults to `i-lucide-x`. ```vue ``` ::framework-only #nuxt :::tip{to="https://ui.nuxt.com/getting-started/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/getting-started/icons/vue#theme"} You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key. ::: :: ## Examples ### Control selected item(s) You can control the selected item(s) by using the `default-value` prop or the `v-model` directive, by using the `onSelect` field on each item or by using the `@update:model-value` event. ```vue [CommandPaletteSelectExample.vue] ``` ### Control search term Use the `v-model:search-term` directive to control the search term. ```vue [CommandPaletteSearchTermExample.vue] ``` ::note This example uses the `@update:model-value` event to reset the search term when an item is selected. :: ### With fetched items You can fetch items from an API and use them in the CommandPalette. ```vue [CommandPaletteFetchExample.vue] ``` ### With ignore filter You can set the `ignoreFilter` field to `true` on a group to disable the internal search and use your own search logic. ```vue [CommandPaletteIgnoreFilterExample.vue] ``` ::note This example uses [`refDebounced`](https://vueuse.org/shared/refDebounced/#refdebounced){rel="nofollow"} to debounce the API calls. :: ### With post-filtered items You can use the `postFilter` field on a group to filter items after the search happened. ```vue [CommandPalettePostFilterExample.vue] ``` ::note Start typing to see items with higher level appear. :: ### With custom fuse search You can use the `fuse` prop to override the options of [useFuse](https://vueuse.org/integrations/useFuse){rel="nofollow"} which defaults to: ```ts { fuseOptions: { ignoreLocation: true, threshold: 0.1, keys: ['label', 'suffix'] }, resultLimit: 12, matchAllWhenSearchEmpty: true } ``` ::tip The `fuseOptions` are the options of [Fuse.js](https://www.fusejs.io/api/options.html){rel="nofollow"}, the `resultLimit` is the maximum number of results to return and the `matchAllWhenSearchEmpty` is a boolean to match all items when the search term is empty. :: You can for example set `{ fuseOptions: { includeMatches: true } }`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} to highlight the search term in the items. ```vue [CommandPaletteFuseExample.vue] ``` ### Within a Popover You can use the CommandPalette component inside a [Popover](https://ui.nuxt.com/components/popover)'s content. ```vue [PopoverCommandPaletteExample.vue] ``` ### Within a Modal You can use the CommandPalette component inside a [Modal](https://ui.nuxt.com/components/modal)'s content. ```vue [ModalCommandPaletteExample.vue] ``` ### Within a Drawer You can use the CommandPalette component inside a [Drawer](https://ui.nuxt.com/components/drawer)'s content. ```vue [DrawerCommandPaletteExample.vue] ``` ### Listen open state When using the `close` prop, you can listen to the `update:open` event when the button is clicked. ```vue [CommandPaletteOpenExample.vue] ``` ::note This can be useful when using the CommandPalette inside a [`Modal`](https://ui.nuxt.com/components/modal) for example. :: ### With custom slot Use the `slot` property to customize a specific item or group. You will have access to the following slots: - `#{{ item.slot }}`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-leading`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-label`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-trailing`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ group.slot }}`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ group.slot }}-leading`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ group.slot }}-label`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ group.slot }}-trailing`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} ```vue [CommandPaletteCustomSlotExample.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 CommandPalette component */ interface CommandPaletteProps { /** * The element or component this component should render as. */ as?: any; /** * The icon displayed in the input. */ icon?: string | undefined; /** * The icon displayed when an item is selected. */ selectedIcon?: string | undefined; /** * The placeholder text for the input. */ placeholder?: string | undefined; /** * Automatically focus the input when component is mounted. * @default "true" */ autofocus?: boolean | undefined; /** * Display a close button in the input (useful when inside a Modal for example). * `{ size: 'md', color: 'neutral', variant: 'ghost' }`{lang="ts-type"} */ close?: boolean | Partial | undefined; /** * The icon displayed in the close button. */ closeIcon?: string | undefined; groups?: CommandPaletteGroup[] | undefined; /** * Options for [useFuse](https://vueuse.org/integrations/useFuse). */ fuse?: UseFuseOptions | undefined; /** * The key used to get the label from the item. * @default "\"label\"" */ labelKey?: string | undefined; ui?: { root?: ClassNameValue; input?: ClassNameValue; close?: ClassNameValue; content?: ClassNameValue; viewport?: ClassNameValue; ... 17 more ...; itemLabelSuffix?: ClassNameValue; } | undefined; /** * Whether multiple options can be selected or not. */ multiple?: boolean | undefined; /** * When `true`, prevents the user from interacting with listbox */ disabled?: boolean | undefined; /** * The controlled value of the listbox. Can be binded with with `v-model`. * @default "\"\"" */ modelValue?: AcceptableValue | AcceptableValue[] | undefined; /** * The value of the listbox when initially rendered. Use when you do not need to control the state of the Listbox */ defaultValue?: AcceptableValue | AcceptableValue[] | undefined; /** * When `true`, hover over item will trigger highlight */ highlightOnHover?: boolean | undefined; /** * When `true`, the loading icon will be displayed. */ loading?: boolean | undefined; /** * The icon when the `loading` prop is `true`. */ loadingIcon?: string | undefined; /** * @default "\"\"" */ searchTerm?: string | undefined; } ``` ### Slots ```ts /** * Slots for the CommandPalette component */ interface CommandPaletteSlots { empty(): any; close(): any; item(): any; item-leading(): any; item-label(): any; item-trailing(): any; } ``` ### Emits ```ts /** * Emitted events for the CommandPalette component */ interface CommandPaletteEmits { update:modelValue: (payload: [value: CommandPaletteItem]) => void; highlight: (payload: [payload: { ref: HTMLElement; value: CommandPaletteItem; } | undefined]) => void; entryFocus: (payload: [event: CustomEvent]) => void; leave: (payload: [event: Event]) => void; update:open: (payload: [value: boolean]) => void; update:searchTerm: (payload: [value: string]) => void; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ ui: { commandPalette: { slots: { root: 'flex flex-col min-h-0 min-w-0 divide-y divide-(--ui-border)', input: '[&>input]:h-12', close: '', content: 'relative overflow-hidden flex flex-col', viewport: 'relative divide-y divide-(--ui-border) scroll-py-1 overflow-y-auto flex-1 focus:outline-none', group: 'p-1 isolate', empty: 'py-6 text-center text-sm text-(--ui-text-muted)', label: 'p-1.5 text-xs font-semibold text-(--ui-text-highlighted)', item: 'group relative w-full flex items-center gap-1.5 p-1.5 text-sm select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75', itemLeadingIcon: 'shrink-0 size-5', itemLeadingAvatar: 'shrink-0', itemLeadingAvatarSize: '2xs', itemLeadingChip: 'shrink-0 size-5', itemLeadingChipSize: 'md', itemTrailing: 'ms-auto inline-flex gap-1.5 items-center', itemTrailingIcon: 'shrink-0 size-5', itemTrailingHighlightedIcon: 'shrink-0 size-5 text-(--ui-text-dimmed) hidden group-data-highlighted:inline-flex', itemTrailingKbds: 'hidden lg:inline-flex items-center shrink-0 gap-0.5', itemTrailingKbdsSize: 'md', itemLabel: 'truncate space-x-1 rtl:space-x-reverse text-(--ui-text-dimmed)', itemLabelBase: 'text-(--ui-text-highlighted) [&>mark]:text-(--ui-bg) [&>mark]:bg-(--ui-primary)', itemLabelPrefix: 'text-(--ui-text)', itemLabelSuffix: 'text-(--ui-text-dimmed) [&>mark]:text-(--ui-bg) [&>mark]:bg-(--ui-primary)' }, variants: { active: { true: { item: 'text-(--ui-text-highlighted) before:bg-(--ui-bg-elevated)', itemLeadingIcon: 'text-(--ui-text)' }, false: { item: [ 'text-(--ui-text) data-highlighted:not-data-disabled:text-(--ui-text-highlighted) data-highlighted:not-data-disabled:before:bg-(--ui-bg-elevated)/50', 'transition-colors before:transition-colors' ], itemLeadingIcon: [ 'text-(--ui-text-dimmed) group-data-highlighted:not-group-data-disabled:text-(--ui-text)', 'transition-colors' ] } }, loading: { true: { itemLeadingIcon: 'animate-spin' } } } } } }) ``` # Container ## Usage ```vue [ContainerExample.vue] ``` ## API ### Props ```ts /** * Props for the Container component */ interface ContainerProps { /** * The element or component this component should render as. */ as?: any; } ``` ### Slots ```ts /** * Slots for the Container component */ interface ContainerSlots { default(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ ui: { container: { base: 'w-full max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8' } } }) ``` # ContentNavigation ::warning{to="https://ui.nuxt.com/getting-started/content"} This component is only available when the `@nuxt/content` module is installed. :: ## Usage Use the `navigation` prop with the `navigation`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} value you get when fetching the navigation of your app. ```vue [ContentNavigationExample.vue] ``` ### Type Set the `type` prop to `single` to only allow one item to be open at a time. Defaults to `multiple`. ```vue ``` ### Color Use the `color` prop to change the color of the navigation links. ```vue ``` ### Variant Use the `variant` prop to change the variant of the navigation links. ```vue ``` ### Highlight Use the `highlight` prop to display a highlighted border for the active link. Use the `highlight-color` prop to change the color of the border. It defaults to the `color` prop. ```vue ``` ### Trailing Icon ```vue ``` ## Examples ### Within a layout Use the ContentNavigation component inside a [PageAside](https://ui.nuxt.com/components/page-aside) component within a layout to display the navigation of the page: ```vue [layouts/docs.vue] {11} ``` ### Within a header Use the ContentNavigation component inside the `content` slot of a [Header](https://ui.nuxt.com/components/header) component to display the navigation of the page on mobile: ```vue [components/Header.vue] {9-11} ``` ## API ### Props ```ts /** * Props for the ContentNavigation component */ interface ContentNavigationProps { } ``` ### Slots ```ts /** * Slots for the ContentNavigation component */ interface ContentNavigationSlots { link(): any; link-leading(): any; link-title(): any; link-trailing(): any; } ``` ### Emits ```ts /** * Emitted events for the ContentNavigation component */ interface ContentNavigationEmits { update:modelValue: (payload: string | string[] | undefined) => void; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { contentNavigation: { slots: { root: '', content: 'data-[state=open]:animate-[accordion-down_200ms_ease-out] data-[state=closed]:animate-[accordion-up_200ms_ease-out] overflow-hidden focus:outline-none', list: 'isolate -mx-2.5 -mt-1.5', item: '', listWithChildren: 'ms-5 border-s border-(--ui-border)', itemWithChildren: 'flex flex-col data-[state=open]:mb-1.5', trigger: 'font-semibold', link: 'group relative w-full px-2.5 py-1.5 before:inset-y-px before:inset-x-0 flex items-center gap-1.5 text-sm before:absolute before:z-[-1] before:rounded-[calc(var(--ui-radius)*1.5)] focus:outline-none focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2', linkLeadingIcon: 'shrink-0 size-5', linkTrailing: 'ms-auto inline-flex gap-1.5 items-center', linkTrailingBadge: 'shrink-0', linkTrailingBadgeSize: 'sm', linkTrailingIcon: 'size-5 transform transition-transform duration-200 shrink-0 group-data-[state=open]:rotate-180', linkTitle: 'truncate', linkTitleExternalIcon: 'size-3 align-top text-(--ui-text-dimmed)' }, variants: { color: { primary: { trigger: 'focus-visible:ring-(--ui-primary)', link: 'focus-visible:before:ring-(--ui-primary)' }, secondary: { trigger: 'focus-visible:ring-(--ui-secondary)', link: 'focus-visible:before:ring-(--ui-secondary)' }, success: { trigger: 'focus-visible:ring-(--ui-success)', link: 'focus-visible:before:ring-(--ui-success)' }, info: { trigger: 'focus-visible:ring-(--ui-info)', link: 'focus-visible:before:ring-(--ui-info)' }, warning: { trigger: 'focus-visible:ring-(--ui-warning)', link: 'focus-visible:before:ring-(--ui-warning)' }, error: { trigger: 'focus-visible:ring-(--ui-error)', link: 'focus-visible:before:ring-(--ui-error)' }, neutral: { trigger: 'focus-visible:ring-(--ui-border-inverted)', link: 'focus-visible:before:ring-(--ui-border-inverted)' } }, highlightColor: { primary: '', secondary: '', success: '', info: '', warning: '', error: '', neutral: '' }, variant: { pill: '', link: '' }, active: { true: { link: 'font-medium' }, false: { link: 'text-(--ui-text-muted)', linkLeadingIcon: 'text-(--ui-text-dimmed)' } }, disabled: { true: { link: 'cursor-not-allowed opacity-75' } }, highlight: { true: {} }, level: { true: { item: 'ps-1.5 -ms-px', itemWithChildren: 'ps-1.5 -ms-px' } } }, compoundVariants: [ { highlight: true, level: true, class: { link: [ 'after:absolute after:-left-1.5 after:inset-y-0.5 after:block after:w-px after:rounded-full', 'after:transition-colors' ] } }, { disabled: false, active: false, variant: 'pill', class: { link: [ 'hover:text-(--ui-text-highlighted) hover:before:bg-(--ui-bg-elevated)/50 data-[state=open]:text-(--ui-text-highlighted)', 'transition-colors before:transition-colors' ], linkLeadingIcon: [ 'group-hover:text-(--ui-text) group-data-[state=open]:text-(--ui-text)', 'transition-colors' ] } }, { color: 'primary', variant: 'pill', active: true, class: { link: 'text-(--ui-primary)', linkLeadingIcon: 'text-(--ui-primary) group-data-[state=open]:text-(--ui-primary)' } }, { color: 'secondary', variant: 'pill', active: true, class: { link: 'text-(--ui-secondary)', linkLeadingIcon: 'text-(--ui-secondary) group-data-[state=open]:text-(--ui-secondary)' } }, { color: 'success', variant: 'pill', active: true, class: { link: 'text-(--ui-success)', linkLeadingIcon: 'text-(--ui-success) group-data-[state=open]:text-(--ui-success)' } }, { color: 'info', variant: 'pill', active: true, class: { link: 'text-(--ui-info)', linkLeadingIcon: 'text-(--ui-info) group-data-[state=open]:text-(--ui-info)' } }, { color: 'warning', variant: 'pill', active: true, class: { link: 'text-(--ui-warning)', linkLeadingIcon: 'text-(--ui-warning) group-data-[state=open]:text-(--ui-warning)' } }, { color: 'error', variant: 'pill', active: true, class: { link: 'text-(--ui-error)', linkLeadingIcon: 'text-(--ui-error) group-data-[state=open]:text-(--ui-error)' } }, { color: 'neutral', variant: 'pill', active: true, class: { link: 'text-(--ui-text-highlighted)', linkLeadingIcon: 'text-(--ui-text-highlighted) group-data-[state=open]:text-(--ui-text-highlighted)' } }, { variant: 'pill', active: true, highlight: false, class: { link: 'before:bg-(--ui-bg-elevated)' } }, { variant: 'pill', active: true, highlight: true, class: { link: [ 'hover:before:bg-(--ui-bg-elevated)/50', 'before:transition-colors' ] } }, { disabled: false, active: false, variant: 'link', class: { link: [ 'hover:text-(--ui-text-highlighted) data-[state=open]:text-(--ui-text-highlighted)', 'transition-colors' ], linkLeadingIcon: [ 'group-hover:text-(--ui-text) group-data-[state=open]:text-(--ui-text)', 'transition-colors' ] } }, { color: 'primary', variant: 'link', active: true, class: { link: 'text-(--ui-primary)', linkLeadingIcon: 'text-(--ui-primary) group-data-[state=open]:text-(--ui-primary)' } }, { color: 'secondary', variant: 'link', active: true, class: { link: 'text-(--ui-secondary)', linkLeadingIcon: 'text-(--ui-secondary) group-data-[state=open]:text-(--ui-secondary)' } }, { color: 'success', variant: 'link', active: true, class: { link: 'text-(--ui-success)', linkLeadingIcon: 'text-(--ui-success) group-data-[state=open]:text-(--ui-success)' } }, { color: 'info', variant: 'link', active: true, class: { link: 'text-(--ui-info)', linkLeadingIcon: 'text-(--ui-info) group-data-[state=open]:text-(--ui-info)' } }, { color: 'warning', variant: 'link', active: true, class: { link: 'text-(--ui-warning)', linkLeadingIcon: 'text-(--ui-warning) group-data-[state=open]:text-(--ui-warning)' } }, { color: 'error', variant: 'link', active: true, class: { link: 'text-(--ui-error)', linkLeadingIcon: 'text-(--ui-error) group-data-[state=open]:text-(--ui-error)' } }, { color: 'neutral', variant: 'link', active: true, class: { link: 'text-(--ui-text-highlighted)', linkLeadingIcon: 'text-(--ui-text-highlighted) group-data-[state=open]:text-(--ui-text-highlighted)' } }, { highlightColor: 'primary', highlight: true, level: true, active: true, class: { link: 'after:bg-(--ui-primary)' } }, { highlightColor: 'secondary', highlight: true, level: true, active: true, class: { link: 'after:bg-(--ui-secondary)' } }, { highlightColor: 'success', highlight: true, level: true, active: true, class: { link: 'after:bg-(--ui-success)' } }, { highlightColor: 'info', highlight: true, level: true, active: true, class: { link: 'after:bg-(--ui-info)' } }, { highlightColor: 'warning', highlight: true, level: true, active: true, class: { link: 'after:bg-(--ui-warning)' } }, { highlightColor: 'error', highlight: true, level: true, active: true, class: { link: 'after:bg-(--ui-error)' } }, { highlightColor: 'neutral', highlight: true, level: true, active: true, class: { link: 'after:bg-(--ui-bg-inverted)' } } ], defaultVariants: { color: 'primary', highlightColor: 'primary', variant: 'pill' } } } }) ``` # ContentSearch ::warning{to="https://ui.nuxt.com/getting-started/content"} This component is only available when the `@nuxt/content` module is installed. :: ## Usage The ContentSearch component extends the [CommandPalette](https://ui.nuxt.com/components/command-palette) component, so you can pass any property such as `icon`, `placeholder`, etc. Use the `files` and `navigation` props with the `files`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} and `navigation`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} values you fetched using the `queryCollectionSearchSections` and `queryCollectionNavigation` composables from `@nuxt/content`. ```vue [ContentSearchExample.vue] ``` ::tip You can open the CommandPalette by pressing `` ``, by using the [ContentSearchButton](https://ui.nuxt.com/components/content-search-button) component or by using the `useContentSearch` composable: `const { open } = useContentSearch()`{className="language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts"}. :: ### Shortcut Use the `shortcut` prop to change the shortcut used in [defineShortcuts](https://ui.nuxt.com/composables/define-shortcuts) to open the ContentSearch component. Defaults to `meta_k` (`` ``). ```vue [app.vue] {6} ``` ### Color Mode By default, a group of commands will be added to the command palette so you can switch between light and dark mode. This will only take effect if the `colorMode` is not forced in a specific page which can be achieved through `definePageMeta`: ```vue [pages/index.vue] ``` You can disable this behavior by setting the `color-mode` prop to `false`: ```vue [app.vue] {6} ``` ## Examples ### Within `app.vue` Use the ContentSearch component in your `app.vue` or in a layout: ```vue [app.vue] ``` ::tip It is recommended to wrap the `ContentSearch` component in a [ClientOnly](https://nuxt.com/docs/api/components/client-only){rel="nofollow"} component so it's not rendered on the server. :: ## API ### Props ```ts /** * Props for the ContentSearch component */ interface ContentSearchProps { } ``` ### Slots ```ts /** * Slots for the ContentSearch component */ interface ContentSearchSlots { empty(): any; close(): any; item(): any; item-leading(): any; item-label(): any; item-trailing(): any; content(): any; } ``` ### Emits ```ts /** * Emitted events for the ContentSearch component */ interface ContentSearchEmits { update:searchTerm: (payload: string) => void; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { contentSearch: { slots: { modal: 'sm:max-w-3xl sm:h-[28rem]', input: '[&>input]:text-base/5' } } } }) ``` # ContentSearchButton ::warning{to="https://ui.nuxt.com/getting-started/content"} This component is only available when the `@nuxt/content` module is installed. :: ## Usage The ContentSearchButton component is used to open the [ContentSearch](https://ui.nuxt.com/components/content-search) modal. ```vue ``` It extends the [Button](https://ui.nuxt.com/components/button) component, so you can pass any property such as `color`, `variant`, `size`, etc. ```vue ``` ::note{to="https://ui.nuxt.com/#collapsed"} The button defaults to `color="neutral"` and `variant="outline"` when not collapsed, `variant="ghost"` when collapsed. :: ### Collapsed Use the `collapsed` prop to show the button's label and [kbds](https://ui.nuxt.com/#kbds). Defaults to `true`. ```vue ``` ### Kbds Use the `kbds` prop to display keyboard keys in the button. Defaults to `['meta', 'K']`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} to match the default shortcut of the [ContentSearch](https://ui.nuxt.com/components/content-search#shortcut) component. ```vue ``` ## API ### Props ```ts /** * Props for the ContentSearchButton component */ interface ContentSearchButtonProps { /** * @default "\"neutral\"" */ color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined; /** * @default "true" */ collapsed?: boolean | undefined; /** * @default "[\"meta\", \"k\"]" */ kbds?: (string | undefined)[] | KbdProps[] | undefined; /** * The icon displayed in the button. */ icon?: string | undefined; /** * The label displayed in the button. */ label?: string | undefined; size?: "md" | "xs" | "sm" | "lg" | "xl" | undefined; ui?: ({ base?: ClassNameValue; trailing?: ClassNameValue; } & { base?: ClassNameValue; label?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; trailingIcon?: ClassNameValue; }) | undefined; /** * The variant of the button. * Defaults to 'outline' when not collapsed, 'ghost' when collapsed. */ variant?: "link" | "solid" | "outline" | "soft" | "subtle" | "ghost" | undefined; } ``` ### Slots ```ts /** * Slots for the ContentSearchButton component */ interface ContentSearchButtonSlots { leading(): any; default(): any; trailing(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { contentSearchButton: { slots: { base: '', trailing: 'hidden lg:flex items-center gap-0.5 ms-auto' } } } }) ``` # ContentSurround ::warning{to="https://ui.nuxt.com/getting-started/content"} This component is only available when the `@nuxt/content` module is installed. :: ## Usage Use the `surround` prop with the `surround`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} value you get when fetching a page surround. ```vue [ContentSurroundExample.vue] ``` ### Prev / Next Use the `prev-icon` and `next-icon` props to customize the buttons [Icon](https://ui.nuxt.com/components/icon). ```vue ``` ## Examples ### Within a page Use the ContentSurround component in a page to display the prev and next links: ```vue [pages/[...slug\\].vue] {19} ``` ## API ### Props ```ts /** * Props for the ContentSurround component */ interface ContentSurroundProps { /** * The element or component this component should render as. */ as?: any; /** * The icon displayed in the prev link. */ prevIcon?: string | undefined; /** * The icon displayed in the next link. */ nextIcon?: string | undefined; surround?: ContentSurroundLink[] | undefined; ui?: { root?: ClassNameValue; link?: ClassNameValue; linkLeading?: ClassNameValue; linkLeadingIcon?: ClassNameValue; linkTitle?: ClassNameValue; linkDescription?: ClassNameValue; } | undefined; } ``` ### Slots ```ts /** * Slots for the ContentSurround component */ interface ContentSurroundSlots { link(): any; link-leading(): any; link-title(): any; link-description(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { contentSurround: { slots: { root: 'grid grid-cols-1 sm:grid-cols-2 gap-8', link: [ 'group block px-6 py-8 rounded-[calc(var(--ui-radius)*2)] border border-(--ui-border) hover:bg-(--ui-bg-elevated)/50 focus-visible:outline-(--ui-primary)', 'transition-colors' ], linkLeading: [ 'inline-flex items-center rounded-full p-1.5 bg-(--ui-bg-elevated) group-hover:bg-(--ui-primary)/10 ring ring-(--ui-border-accented) mb-4 group-hover:ring-(--ui-primary)/50', 'transition' ], linkLeadingIcon: [ 'size-5 shrink-0 text-(--ui-text-highlighted) group-hover:text-(--ui-primary)', 'transition-[color,translate]' ], linkTitle: 'font-medium text-[15px] text-(--ui-text-highlighted) mb-1 truncate', linkDescription: 'text-sm text-(--ui-text-muted) line-clamp-2' }, variants: { direction: { left: { linkLeadingIcon: [ 'group-active:-translate-x-0.5' ] }, right: { link: 'text-right', linkLeadingIcon: [ 'group-active:translate-x-0.5' ] } } } } } }) ``` # ContentToc ::warning{to="https://ui.nuxt.com/getting-started/content"} This component is only available when the `@nuxt/content` module is installed. :: ## Usage Use the `links` prop with the `page?.body?.toc?.links`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} you get when fetching a page. ```vue [ContentTocExample.vue] ``` ### Title Use the `title` prop to change the title of the Table of Contents. ```vue ``` ### Color Use the `color` prop to change the color of the links. ```vue ``` ### Highlight Use the `highlight` prop to display a highlighted border for the active item. Use the `highlight-color` prop to change the color of the border. It defaults to the `color` prop. ```vue ``` ## Examples ### Within a page Use the ContentToc component in a page to display the Table of Contents: ```vue [pages/[...slug\\].vue] {22-24} ``` ## API ### Props ```ts /** * Props for the ContentToc component */ interface ContentTocProps { } ``` ### Slots ```ts /** * Slots for the ContentToc component */ interface ContentTocSlots { leading(): any; default(): any; trailing(): any; content(): any; link(): any; top(): any; bottom(): any; } ``` ### Emits ```ts /** * Emitted events for the ContentToc component */ interface ContentTocEmits { update:open: (payload: boolean) => void; move: (payload: string) => void; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { contentToc: { slots: { root: 'sticky top-(--ui-header-height) bg-(--ui-bg)/75 lg:bg-[initial] backdrop-blur -mx-4 px-4 sm:px-6 sm:-mx-6 overflow-y-auto max-h-[calc(100vh-var(--ui-header-height))]', container: 'pt-4 sm:pt-6 pb-2.5 sm:pb-4.5 lg:py-8 border-b border-dashed border-(--ui-border) lg:border-0 flex flex-col', top: '', bottom: 'mt-6 hidden lg:flex lg:flex-col gap-6', trigger: 'group text-sm font-semibold flex-1 flex items-center gap-1.5 py-1.5 -mt-1.5 focus-visible:outline-(--ui-primary)', title: 'truncate', trailing: 'ms-auto inline-flex gap-1.5 items-center', trailingIcon: 'size-5 transform transition-transform duration-200 shrink-0 group-data-[state=open]:rotate-180 lg:hidden', content: 'data-[state=open]:animate-[collapsible-down_200ms_ease-out] data-[state=closed]:animate-[collapsible-up_200ms_ease-out] overflow-hidden focus:outline-none', list: 'min-w-0', listWithChildren: 'ms-3', item: 'min-w-0', itemWithChildren: '', link: 'group relative text-sm flex items-center focus-visible:outline-(--ui-primary) py-1', linkText: 'truncate', indicator: 'absolute ms-2.5 transition-[translate,height] duration-200 h-(--indicator-size) translate-y-(--indicator-position) w-px rounded-full' }, variants: { color: { primary: '', secondary: '', success: '', info: '', warning: '', error: '', neutral: '' }, highlightColor: { primary: { indicator: 'bg-(--ui-primary)' }, secondary: { indicator: 'bg-(--ui-secondary)' }, success: { indicator: 'bg-(--ui-success)' }, info: { indicator: 'bg-(--ui-info)' }, warning: { indicator: 'bg-(--ui-warning)' }, error: { indicator: 'bg-(--ui-error)' }, neutral: { indicator: 'bg-(--ui-bg-inverted)' } }, active: { false: { link: [ 'text-(--ui-text-muted) hover:text-(--ui-text)', 'transition-colors' ] } }, highlight: { true: { list: 'ms-2.5 ps-4 border-s border-(--ui-border)', item: '-ms-px' } } }, compoundVariants: [ { color: 'primary', active: true, class: { link: 'text-(--ui-primary)', linkLeadingIcon: 'text-(--ui-primary)' } }, { color: 'secondary', active: true, class: { link: 'text-(--ui-secondary)', linkLeadingIcon: 'text-(--ui-secondary)' } }, { color: 'success', active: true, class: { link: 'text-(--ui-success)', linkLeadingIcon: 'text-(--ui-success)' } }, { color: 'info', active: true, class: { link: 'text-(--ui-info)', linkLeadingIcon: 'text-(--ui-info)' } }, { color: 'warning', active: true, class: { link: 'text-(--ui-warning)', linkLeadingIcon: 'text-(--ui-warning)' } }, { color: 'error', active: true, class: { link: 'text-(--ui-error)', linkLeadingIcon: 'text-(--ui-error)' } }, { color: 'neutral', active: true, class: { link: 'text-(--ui-text-highlighted)', linkLeadingIcon: 'text-(--ui-text-highlighted)' } } ], defaultVariants: { color: 'primary', highlightColor: 'primary' } } } }) ``` # ContextMenu ## Usage Use anything you like in the default slot of the ContextMenu, and right-click on it to display the menu. ### Items Use the `items` prop as an array of objects with the following properties: - `label?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `icon?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `color?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `avatar?: AvatarProps`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `kbds?: string[] | KbdProps[]`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - [`type?: "link" | "label" | "separator" | "checkbox"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-checkbox-items) - [`color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-color-items) - [`checked?: boolean`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-checkbox-items) - `disabled?: boolean`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `class?: any`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - [`slot?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-custom-slot) - `onSelect?(e: Event): void`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - [`onUpdateChecked?(checked: boolean): void`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-checkbox-items) - `children?: ContextMenuItem[] | ContextMenuItem[][]`{className="language-ts-type 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/components/link#props) component such as `to`, `target`, etc. ```vue ``` ::note You can also pass an array of arrays to the `items` prop to create separated groups of items. :: ::tip Each item can take a `children` array of objects with the same properties as the `items` prop to create a nested menu which can be controlled using the `open`, `defaultOpen` and `content` properties. :: ### Size Use the `size` prop to change the size of the ContextMenu. ```vue ``` ### Disabled Use the `disabled` prop to disable the ContextMenu. ```vue ``` ## Examples ### With checkbox items You can use the `type` property with `checkbox` and use the `checked` / `onUpdateChecked` properties to control the checked state of the item. ```vue [ContextMenuCheckboxItemsExample.vue] ``` ::note To ensure reactivity for the `checked` state of items, it's recommended to wrap your `items` array inside a `computed`. :: ### With color items You can use the `color` property to highlight certain items with a color. ```vue [ContextMenuColorItemsExample.vue] ``` ### With custom slot Use the `slot` property to customize a specific item. You will have access to the following slots: - `#{{ item.slot }}`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-leading`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-label`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-trailing`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} ```vue [ContextMenuCustomSlotExample.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. :: ### Extract shortcuts When you have some items with `kbds` property (displaying some [Kbd](https://ui.nuxt.com/components/kbd)), you can easily make them work with the [defineShortcuts](https://ui.nuxt.com/composables/define-shortcuts) composable. Inside the `defineShortcuts` composable, there is an `extractShortcuts` utility that will extract the shortcuts recursively from the items and return an object that you can pass to `defineShortcuts`. It will automatically call the `select` function of the item when the shortcut is pressed. ```vue ``` ::note In this example, `` ``, `` `` ``, `` `` ``, `` `` ``, `` `` `` and `` `` `` would trigger the `select` function of the corresponding item. :: ## API ### Props ```ts /** * Props for the ContextMenu component */ interface ContextMenuProps { size?: "sm" | "md" | "xs" | "lg" | "xl" | undefined; items?: ArrayOrNested | undefined; /** * The icon displayed when an item is checked. */ checkedIcon?: string | undefined; /** * The icon displayed when an item is loading. */ loadingIcon?: string | undefined; /** * The icon displayed when the item is an external link. * Set to `false` to hide the external icon. * @default "true" */ externalIcon?: string | boolean | undefined; /** * The content of the menu. */ content?: (Omit & Partial>) | undefined; /** * Render the menu in a portal. * @default "true" */ portal?: boolean | undefined; /** * The key used to get the label from the item. * @default "\"label\"" */ labelKey?: keyof ContextMenuItem | undefined; disabled?: boolean | undefined; ui?: { content?: ClassNameValue; group?: ClassNameValue; label?: ClassNameValue; separator?: ClassNameValue; item?: ClassNameValue; ... 8 more ...; itemLabelExternalIcon?: ClassNameValue; } | undefined; /** * The modality of the dropdown menu. * * When set to `true`, interaction with outside elements will be disabled and only menu content will be visible to screen readers. * @default "true" */ modal?: boolean | undefined; } ``` ### Slots ```ts /** * Slots for the ContextMenu component */ interface ContextMenuSlots { default(): any; item(): any; item-leading(): any; item-label(): any; item-trailing(): any; } ``` ### Emits ```ts /** * Emitted events for the ContextMenu component */ interface ContextMenuEmits { update:open: (payload: [payload: boolean]) => void; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ ui: { contextMenu: { slots: { content: 'min-w-32 bg-(--ui-bg) shadow-lg rounded-md ring ring-(--ui-border) divide-y divide-(--ui-border) overflow-y-auto scroll-py-1 data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]', group: 'p-1 isolate', label: 'w-full flex items-center font-semibold text-(--ui-text-highlighted)', separator: '-mx-1 my-1 h-px bg-(--ui-border)', item: 'group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75', itemLeadingIcon: 'shrink-0', itemLeadingAvatar: 'shrink-0', itemLeadingAvatarSize: '', itemTrailing: 'ms-auto inline-flex gap-1.5 items-center', itemTrailingIcon: 'shrink-0', itemTrailingKbds: 'hidden lg:inline-flex items-center shrink-0', itemTrailingKbdsSize: '', itemLabel: 'truncate', itemLabelExternalIcon: 'inline-block size-3 align-top text-(--ui-text-dimmed)' }, variants: { color: { primary: '', secondary: '', success: '', info: '', warning: '', error: '', neutral: '' }, active: { true: { item: 'text-(--ui-text-highlighted) before:bg-(--ui-bg-elevated)', itemLeadingIcon: 'text-(--ui-text)' }, false: { item: [ 'text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-[state=open]:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 data-[state=open]:before:bg-(--ui-bg-elevated)/50', 'transition-colors before:transition-colors' ], itemLeadingIcon: [ 'text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) group-data-[state=open]:text-(--ui-text)', 'transition-colors' ] } }, loading: { true: { itemLeadingIcon: 'animate-spin' } }, size: { xs: { label: 'p-1 text-xs gap-1', item: 'p-1 text-xs gap-1', itemLeadingIcon: 'size-4', itemLeadingAvatarSize: '3xs', itemTrailingIcon: 'size-4', itemTrailingKbds: 'gap-0.5', itemTrailingKbdsSize: 'sm' }, sm: { label: 'p-1.5 text-xs gap-1.5', item: 'p-1.5 text-xs gap-1.5', itemLeadingIcon: 'size-4', itemLeadingAvatarSize: '3xs', itemTrailingIcon: 'size-4', itemTrailingKbds: 'gap-0.5', itemTrailingKbdsSize: 'sm' }, md: { label: 'p-1.5 text-sm gap-1.5', item: 'p-1.5 text-sm gap-1.5', itemLeadingIcon: 'size-5', itemLeadingAvatarSize: '2xs', itemTrailingIcon: 'size-5', itemTrailingKbds: 'gap-0.5', itemTrailingKbdsSize: 'md' }, lg: { label: 'p-2 text-sm gap-2', item: 'p-2 text-sm gap-2', itemLeadingIcon: 'size-5', itemLeadingAvatarSize: '2xs', itemTrailingIcon: 'size-5', itemTrailingKbds: 'gap-1', itemTrailingKbdsSize: 'md' }, xl: { label: 'p-2 text-base gap-2', item: 'p-2 text-base gap-2', itemLeadingIcon: 'size-6', itemLeadingAvatarSize: 'xs', itemTrailingIcon: 'size-6', itemTrailingKbds: 'gap-1', itemTrailingKbdsSize: 'lg' } } }, compoundVariants: [ { color: 'primary', active: false, class: { item: 'text-(--ui-primary) data-highlighted:text-(--ui-primary) data-highlighted:before:bg-(--ui-primary)/10 data-[state=open]:before:bg-(--ui-primary)/10', itemLeadingIcon: 'text-(--ui-primary)/75 group-data-highlighted:text-(--ui-primary) group-data-[state=open]:text-(--ui-primary)' } }, { color: 'secondary', active: false, class: { item: 'text-(--ui-secondary) data-highlighted:text-(--ui-secondary) data-highlighted:before:bg-(--ui-secondary)/10 data-[state=open]:before:bg-(--ui-secondary)/10', itemLeadingIcon: 'text-(--ui-secondary)/75 group-data-highlighted:text-(--ui-secondary) group-data-[state=open]:text-(--ui-secondary)' } }, { color: 'success', active: false, class: { item: 'text-(--ui-success) data-highlighted:text-(--ui-success) data-highlighted:before:bg-(--ui-success)/10 data-[state=open]:before:bg-(--ui-success)/10', itemLeadingIcon: 'text-(--ui-success)/75 group-data-highlighted:text-(--ui-success) group-data-[state=open]:text-(--ui-success)' } }, { color: 'info', active: false, class: { item: 'text-(--ui-info) data-highlighted:text-(--ui-info) data-highlighted:before:bg-(--ui-info)/10 data-[state=open]:before:bg-(--ui-info)/10', itemLeadingIcon: 'text-(--ui-info)/75 group-data-highlighted:text-(--ui-info) group-data-[state=open]:text-(--ui-info)' } }, { color: 'warning', active: false, class: { item: 'text-(--ui-warning) data-highlighted:text-(--ui-warning) data-highlighted:before:bg-(--ui-warning)/10 data-[state=open]:before:bg-(--ui-warning)/10', itemLeadingIcon: 'text-(--ui-warning)/75 group-data-highlighted:text-(--ui-warning) group-data-[state=open]:text-(--ui-warning)' } }, { color: 'error', active: false, class: { item: 'text-(--ui-error) data-highlighted:text-(--ui-error) data-highlighted:before:bg-(--ui-error)/10 data-[state=open]:before:bg-(--ui-error)/10', itemLeadingIcon: 'text-(--ui-error)/75 group-data-highlighted:text-(--ui-error) group-data-[state=open]:text-(--ui-error)' } }, { color: 'primary', active: true, class: { item: 'text-(--ui-primary) before:bg-(--ui-primary)/10', itemLeadingIcon: 'text-(--ui-primary)' } }, { color: 'secondary', active: true, class: { item: 'text-(--ui-secondary) before:bg-(--ui-secondary)/10', itemLeadingIcon: 'text-(--ui-secondary)' } }, { color: 'success', active: true, class: { item: 'text-(--ui-success) before:bg-(--ui-success)/10', itemLeadingIcon: 'text-(--ui-success)' } }, { color: 'info', active: true, class: { item: 'text-(--ui-info) before:bg-(--ui-info)/10', itemLeadingIcon: 'text-(--ui-info)' } }, { color: 'warning', active: true, class: { item: 'text-(--ui-warning) before:bg-(--ui-warning)/10', itemLeadingIcon: 'text-(--ui-warning)' } }, { color: 'error', active: true, class: { item: 'text-(--ui-error) before:bg-(--ui-error)/10', itemLeadingIcon: 'text-(--ui-error)' } } ], defaultVariants: { size: 'md' } } } }) ``` # DashboardGroup ## Usage The DashboardGroup component is the main layout that wraps the [DashboardSidebar](https://ui.nuxt.com/components/dashboard-sidebar) and [DashboardPanel](https://ui.nuxt.com/components/dashboard-panel) components to create a responsive dashboard interface. Use it in a layout or in your `app.vue`: ```vue [layouts/dashboard.vue] {2,6} ``` ## API ### Props ```ts /** * Props for the DashboardGroup component */ interface DashboardGroupProps { /** * @default "\"cookie\"" */ storage?: "cookie" | "local" | undefined; /** * @default "\"dashboard\"" */ storageKey?: string | undefined; /** * @default "true" */ persistent?: boolean | undefined; /** * @default "\"%\"" */ unit?: "%" | "rem" | "px" | undefined; /** * The element or component this component should render as. */ as?: any; } ``` ### Slots ```ts /** * Slots for the DashboardGroup component */ interface DashboardGroupSlots { default(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { dashboardGroup: { base: 'fixed inset-0 flex overflow-hidden' } } }) ``` # DashboardNavbar ## Usage The DashboardNavbar component is a responsive navigation bar that integrates with the [DashboardSidebar](https://ui.nuxt.com/components/dashboard-sidebar) component. It includes a mobile toggle button to enable responsive navigation in dashboard layouts. Use it inside the `header` slot of the [DashboardPanel](https://ui.nuxt.com/components/dashboard-panel) component: ```vue [pages/index.vue] {9-11} ``` Use the `left`, `default` and `right` slots to customize the navbar. ```vue [DashboardNavbarExample.vue] ``` ::note In this example, we use the [Tabs](https://ui.nuxt.com/components/tabs) component in the right slot to display some tabs. :: ### Title Use the `title` prop to set the title of the navbar. ```vue ``` ### Icon Use the `icon` prop to set the icon of the navbar. ```vue ``` ### Toggle Use the `toggle` prop to customize the toggle button displayed on mobile that opens the [DashboardSidebar](https://ui.nuxt.com/components/dashboard-sidebar) component. You can pass any property from the [Button](https://ui.nuxt.com/components/button) component to customize it. ```vue [DashboardNavbarToggleExample.vue] ``` ### Toggle Side Use the `toggle-side` prop to change the side of the toggle button. Defaults to `right`. ```vue [DashboardNavbarToggleSideExample.vue] ``` ## API ### Props ```ts /** * Props for the DashboardNavbar component */ interface DashboardNavbarProps { /** * @default "true" */ toggle?: boolean | Partial | undefined; /** * @default "\"left\"" */ toggleSide?: "right" | "left" | undefined; /** * The icon displayed next to the title. */ icon?: string | undefined; /** * The element or component this component should render as. */ as?: any; ui?: { root?: ClassNameValue; left?: ClassNameValue; icon?: ClassNameValue; title?: ClassNameValue; center?: ClassNameValue; right?: ClassNameValue; toggle?: ClassNameValue; } | undefined; title?: string | undefined; } ``` ### Slots ```ts /** * Slots for the DashboardNavbar component */ interface DashboardNavbarSlots { title(): any; leading(): any; trailing(): any; left(): any; default(): any; right(): any; toggle(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { dashboardNavbar: { slots: { root: 'h-(--ui-header-height) shrink-0 flex items-center justify-between border-b border-(--ui-border) px-4 sm:px-6 gap-1.5', left: 'flex items-center gap-1.5 min-w-0', icon: 'shrink-0 size-5 self-center me-1.5', title: 'flex items-center gap-1.5 font-semibold text-(--ui-text-highlighted) truncate', center: 'hidden lg:flex', right: 'flex items-center shrink-0 gap-1.5', toggle: '' }, variants: { toggleSide: { left: { toggle: '' }, right: { toggle: '' } } } } } }) ``` # DashboardPanel ## Usage The DashboardPanel component is used to display a panel. Its state (size, collapsed, etc.) will be saved based on the `storage` and `storage-key` props you provide to the [DashboardGroup](https://ui.nuxt.com/components/dashboard-group#props) component. Use it inside the default slot of the [DashboardGroup](https://ui.nuxt.com/components/dashboard-group) component, you can put multiple panels next to each other: ```vue [pages/index.vue] {8,10} ``` ::caution It is recommended to set an `id` when using multiple panels in different pages to avoid conflicts. :: Use the `header`, `body` and `footer` slots to customize the panel or the default slot if you don't want a scrollable body with padding. ```vue [DashboardPanelExample.vue] ``` ::note Most of the time, you will use the [`DashboardNavbar`](https://ui.nuxt.com/components/dashboard-navbar) component in the `header` slot. :: ### Resizable Use the `resizable` prop to make the panel resizable. ```vue ``` ### Size Use the `min-size`, `max-size` and `default-size` props to customize the size of the panel. ```vue ``` ## API ### Props ```ts /** * Props for the DashboardPanel component */ interface DashboardPanelProps { /** * @default "15" */ minSize?: number | undefined; /** * @default "false" */ resizable?: boolean | undefined; ui?: { root?: ClassNameValue; body?: ClassNameValue; handle?: ClassNameValue; } | undefined; /** * The id of the panel. */ id?: string | undefined; /** * The maximum size of the panel. */ maxSize?: number | undefined; /** * The default size of the panel. */ defaultSize?: number | undefined; } ``` ### Slots ```ts /** * Slots for the DashboardPanel component */ interface DashboardPanelSlots { default(): any; header(): any; body(): any; footer(): any; resize-handle(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { dashboardPanel: { slots: { root: 'relative flex flex-col min-w-0 min-h-svh lg:not-last:border-r lg:not-last:border-(--ui-border) shrink-0', body: 'flex flex-col gap-4 sm:gap-6 flex-1 overflow-y-auto p-4 sm:p-6', handle: '' }, variants: { size: { true: { root: 'w-full lg:w-(--width)' }, false: { root: 'flex-1' } } } } } }) ``` # DashboardResizeHandle ## Usage The DashboardResizeHandle component is used by the [DashboardSidebar](https://ui.nuxt.com/components/dashboard-sidebar) and [DashboardPanel](https://ui.nuxt.com/components/dashboard-panel) components. It is automatically displayed when the `resizable` prop is set, **you don't have to add it manually**. ## Examples ### Within `resize-handle` slot Even though this component is automatically displayed when the `resizable` prop is set, you can use the `resize-handle` slot of the [DashboardSidebar](https://ui.nuxt.com/components/dashboard-sidebar) and [DashboardPanel](https://ui.nuxt.com/components/dashboard-panel) components to customize the handle. ::code-group ```vue [layouts/dashboard.vue] {4-10} ``` ```vue [pages/index.vue] {9-15} ``` :: ::note In this example, we add an `after` pseudo-element to display a vertical line on hover. :: ## API ### Props ```ts /** * Props for the DashboardResizeHandle component */ interface DashboardResizeHandleProps { /** * The element or component this component should render as. */ as?: any; } ``` ### Slots ```ts /** * Slots for the DashboardResizeHandle component */ interface DashboardResizeHandleSlots { default(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { dashboardResizeHandle: { base: 'hidden lg:block touch-none select-none cursor-ew-resize relative before:absolute before:inset-y-0 before:-left-1.5 before:-right-1.5' } } }) ``` # DashboardSearch ## Usage The DashboardSearch component extends the [CommandPalette](https://ui.nuxt.com/components/command-palette) component, so you can pass any property such as `icon`, `placeholder`, etc. Use it inside the default slot of the [DashboardGroup](https://ui.nuxt.com/components/dashboard-group) component: ```vue [layouts/dashboard.vue] {3} ``` ::tip You can open the CommandPalette by pressing `` ``, by using the [DashboardSearchButton](https://ui.nuxt.com/components/dashboard-search-button) component or by using a `v-model:open`{className="language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts"} directive. :: ### Shortcut Use the `shortcut` prop to change the shortcut used in [defineShortcuts](https://ui.nuxt.com/composables/define-shortcuts) to open the ContentSearch component. Defaults to `meta_k` (`` ``). ```vue [app.vue] {4} ``` ### Color Mode By default, a group of commands will be added to the command palette so you can switch between light and dark mode. This will only take effect if the `colorMode` is not forced in a specific page which can be achieved through `definePageMeta`: ```vue [pages/index.vue] ``` You can disable this behavior by setting the `color-mode` prop to `false`: ```vue [app.vue] {4} ``` ## API ### Props ```ts /** * Props for the DashboardSearch component */ interface DashboardSearchProps { } ``` ### Slots ```ts /** * Slots for the DashboardSearch component */ interface DashboardSearchSlots { } ``` ### Emits No events available for this component. ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { dashboardSearch: { slots: { modal: 'sm:max-w-3xl sm:h-[28rem]', input: '[&>input]:text-base/5' } } } }) ``` # DashboardSearchButton ## Usage The DashboardSearchButton component is used to open the [DashboardSearch](https://ui.nuxt.com/components/dashboard-search) modal. ```vue ``` It extends the [Button](https://ui.nuxt.com/components/button) component, so you can pass any property such as `color`, `variant`, `size`, etc. ```vue ``` ::note{to="https://ui.nuxt.com/#collapsed"} The button defaults to `color="neutral"` and `variant="outline"` when not collapsed, `variant="ghost"` when collapsed. :: ### Collapsed Use the `collapsed` prop to hide the button's label and [kbds](https://ui.nuxt.com/#kbds). Defaults to `false`. ```vue ``` ::tip{to="https://ui.nuxt.com/components/dashboard-sidebar#slots"} When using the button in the **DashboardSidebar** component, use the `collapsed` slot prop directly. :: ### Kbds Use the `kbds` prop to display keyboard keys in the button. Defaults to `['meta', 'K']`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} to match the default shortcut of the [DashboardSearch](https://ui.nuxt.com/components/dashboard-search#shortcut) component. ```vue ``` ## API ### Props ```ts /** * Props for the DashboardSearchButton component */ interface DashboardSearchButtonProps { /** * @default "\"neutral\"" */ color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined; /** * @default "false" */ collapsed?: boolean | undefined; /** * @default "[\"meta\", \"k\"]" */ kbds?: (string | undefined)[] | KbdProps[] | undefined; /** * The icon displayed in the button. */ icon?: string | undefined; /** * The label displayed in the button. */ label?: string | undefined; size?: "md" | "xs" | "sm" | "lg" | "xl" | undefined; ui?: ({ base?: ClassNameValue; trailing?: ClassNameValue; } & { base?: ClassNameValue; label?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; trailingIcon?: ClassNameValue; }) | undefined; /** * The variant of the button. * Defaults to 'outline' when not collapsed, 'ghost' when collapsed. */ variant?: "link" | "solid" | "outline" | "soft" | "subtle" | "ghost" | undefined; } ``` ### Slots ```ts /** * Slots for the DashboardSearchButton component */ interface DashboardSearchButtonSlots { leading(): any; default(): any; trailing(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { dashboardSearchButton: { slots: { base: '', trailing: 'hidden lg:flex items-center gap-0.5 ms-auto' } } } }) ``` # DashboardSidebar ## Usage The DashboardSidebar component is used to display a sidebar. Its state (size, collapsed, etc.) will be saved based on the `storage` and `storage-key` props you provide to the [DashboardGroup](https://ui.nuxt.com/components/dashboard-group#props) component. Use it inside the default slot of the [DashboardGroup](https://ui.nuxt.com/components/dashboard-group) component: ```vue [layouts/dashboard.vue] {3} ``` Use the `left`, `default` and `right` slots to customize the sidebar and the `body` or `content` slots to customize the sidebar menu. ```vue [DashboardSidebarExample.vue] ``` ::note Drag the sidebar near the left edge of the screen to collapse it. :: ### Resizable Use the `resizable` prop to make the sidebar resizable. ```vue ``` ### Collapsible Use the `collapsible` prop to make the sidebar collapsible when dragging near the edge of the screen. ::warning The [`DashboardSidebarCollapse`](https://ui.nuxt.com/components/dashboard-sidebar-collapse) component will have no effect if the sidebar is not **collapsible**. :: ```vue ``` ::tip{to="https://ui.nuxt.com/#slots"} You can access the `collapsed` state in the slot props to customize the content of the sidebar when it is collapsed. :: ### Size Use the `min-size`, `max-size`, `default-size` and `collapsed-size` props to customize the size of the sidebar. ```vue ``` ::note The `collapsed-size` prop is set to `0` by default but the sidebar has a `min-w-16` to make sure it is visible. :: ### Side Use the `side` prop to change the side of the sidebar. Defaults to `left`. ```vue ``` ### Mode Use the `mode` prop to change the mode of the sidebar menu. Defaults to `slideover`. Use the `body` slot to fill the menu body (under the header) or the `content` slot to fill the entire menu. ::tip{to="https://ui.nuxt.com/#props"} You can use the `menu` prop to customize the menu of the sidebar, it will adapt depending on the mode you choose. :: ```vue [DashboardSidebarModeExample.vue] ``` ::note These examples contain the [`DashboardGroup`](https://ui.nuxt.com/components/dashboard-group), [`DashboardPanel`](https://ui.nuxt.com/components/dashboard-panel) and [`DashboardNavbar`](https://ui.nuxt.com/components/dashboard-navbar) components as they are required to demonstrate the sidebar on mobile. :: ### Toggle Use the `toggle` prop to customize the [DashboardSidebarToggle](https://ui.nuxt.com/components/dashboard-sidebar-toggle) component displayed on mobile. You can pass any property from the [Button](https://ui.nuxt.com/components/button) component to customize it. ```vue [DashboardSidebarToggleExample.vue] ``` ### Toggle Side Use the `toggle-side` prop to change the side of the toggle button. Defaults to `left`. ```vue [DashboardSidebarToggleSideExample.vue] ``` ## Examples ### Control open state You can control the open state by using the `open` prop or the `v-model:open` directive. ```vue [DashboardSidebarOpenExample.vue] ``` ::note In this example, leveraging [`defineShortcuts`](https://ui.nuxt.com/composables/define-shortcuts), you can toggle the open state of the DashboardSidebar by pressing ``. :: ### Control collapsed state You can control the collapsed state by using the `collapsed` prop or the `v-model:collapsed` directive. ```vue [DashboardSidebarCollapsedExample.vue] ``` ::note In this example, leveraging [`defineShortcuts`](https://ui.nuxt.com/composables/define-shortcuts), you can toggle the collapsed state of the DashboardSidebar by pressing ``. :: ## API ### Props ```ts /** * Props for the DashboardSidebar component */ interface DashboardSidebarProps { } ``` ### Slots ```ts /** * Slots for the DashboardSidebar component */ interface DashboardSidebarSlots { header(): any; default(): any; footer(): any; toggle(): any; content(): any; resize-handle(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { dashboardSidebar: { slots: { root: 'relative hidden lg:flex flex-col min-h-svh min-w-16 w-(--width) shrink-0', header: 'h-(--ui-header-height) shrink-0 flex items-center gap-1.5 px-4', body: 'flex flex-col gap-4 flex-1 overflow-y-auto px-4 py-2', footer: 'shrink-0 flex items-center gap-1.5 px-4 py-2', toggle: '', handle: '', content: 'lg:hidden', overlay: 'lg:hidden' }, variants: { menu: { true: { header: 'sm:px-6', body: 'sm:px-6', footer: 'sm:px-6' } }, side: { left: { root: 'border-r border-(--ui-border)' }, right: { root: '' } }, toggleSide: { left: { toggle: '' }, right: { toggle: 'ms-auto' } } } } } }) ``` # DashboardSidebarCollapse ## Usage The DashboardSidebarCollapse component is used to collapse/expand the [DashboardSidebar](https://ui.nuxt.com/components/dashboard-sidebar) component **when its `collapsible` prop is set**. ```vue ``` It extends the [Button](https://ui.nuxt.com/components/button) component, so you can pass any property such as `color`, `variant`, `size`, etc. ```vue ``` ::note The button defaults to `color="neutral"` and `variant="ghost"`. :: ## Examples ### Within `header` slot You can put this component in the `header` slot of the [DashboardSidebar](https://ui.nuxt.com/components/dashboard-sidebar) component and use the `collapsed` prop to hide the left part of the header for example: ```vue [layouts/dashboard.vue] {4-8} ``` ### Within `leading` slot You can put this component in the `leading` slot of the [DashboardNavbar](https://ui.nuxt.com/components/dashboard-navbar) component to display it before the title for example: ```vue [pages/index.vue] {11-13} ``` ## API ### Props ```ts /** * Props for the DashboardSidebarCollapse component */ interface DashboardSidebarCollapseProps { /** * @default "\"neutral\"" */ color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined; /** * @default "\"ghost\"" */ variant?: "link" | "solid" | "outline" | "soft" | "subtle" | "ghost" | undefined; /** * @default "\"left\"" */ side?: "right" | "left" | undefined; disabled?: boolean | undefined; /** * The element or component this component should render as when not a link. */ as?: any; size?: "md" | "xs" | "sm" | "lg" | "xl" | undefined; ui?: { base?: ClassNameValue; label?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; trailingIcon?: ClassNameValue; } | undefined; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { dashboardSidebarCollapse: { base: 'hidden lg:flex', variants: { side: { left: '', right: '' } } } } }) ``` # DashboardSidebarToggle ## Usage The DashboardSidebarToggle component is used by the [DashboardNavbar](https://ui.nuxt.com/components/dashboard-navbar) and [DashboardSidebar](https://ui.nuxt.com/components/dashboard-sidebar) components. It is automatically displayed on mobile to toggle the sidebar, **you don't have to add it manually**. ```vue ``` It extends the [Button](https://ui.nuxt.com/components/button) component, so you can pass any property such as `color`, `variant`, `size`, etc. ```vue ``` ::note The button defaults to `color="neutral"` and `variant="ghost"`. :: ## Examples ### Within `toggle` slot Even though this component is automatically displayed on mobile, you can use the `toggle` slot of the [DashboardNavbar](https://ui.nuxt.com/components/dashboard-navbar) and [DashboardSidebar](https://ui.nuxt.com/components/dashboard-sidebar) components to customize the button. ::code-group ```vue [layouts/dashboard.vue] {4-6} ``` ```vue [pages/index.vue] {11-13} ``` :: ::tip When using the `toggle-side` prop of the `DashboardSidebar` and `DashboardNavbar` components, the button will be displayed on the specified side. :: ## API ### Props ```ts /** * Props for the DashboardSidebarToggle component */ interface DashboardSidebarToggleProps { /** * @default "\"neutral\"" */ color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral" | undefined; /** * @default "\"ghost\"" */ variant?: "link" | "solid" | "outline" | "soft" | "subtle" | "ghost" | undefined; /** * @default "\"left\"" */ side?: "right" | "left" | undefined; disabled?: boolean | undefined; /** * The element or component this component should render as when not a link. */ as?: any; size?: "md" | "xs" | "sm" | "lg" | "xl" | undefined; ui?: { base?: ClassNameValue; label?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; trailingIcon?: ClassNameValue; } | undefined; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { dashboardSidebarToggle: { base: 'lg:hidden', variants: { side: { left: '', right: '' } } } } }) ``` # DashboardToolbar ## Usage The DashboardToolbar component is used to display a toolbar under the [DashboardNavbar](https://ui.nuxt.com/components/dashboard-navbar) component. Use it inside the `header` slot of the [DashboardPanel](https://ui.nuxt.com/components/dashboard-panel) component: ```vue [pages/index.vue] {9-13} ``` Use the `left`, `default` and `right` slots to customize the toolbar. ```vue [DashboardToolbarExample.vue] ``` ::note In this example, we use the [NavigationMenu](https://ui.nuxt.com/components/navigation-menu) component to render some links. :: ## API ### Props ```ts /** * Props for the DashboardToolbar component */ interface DashboardToolbarProps { /** * The element or component this component should render as. */ as?: any; ui?: { root?: ClassNameValue; left?: ClassNameValue; right?: ClassNameValue; } | undefined; } ``` ### Slots ```ts /** * Slots for the DashboardToolbar component */ interface DashboardToolbarSlots { default(): any; left(): any; right(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { dashboardToolbar: { slots: { root: 'shrink-0 flex items-center justify-between border-b border-(--ui-border) px-4 sm:px-6 gap-1.5 overflow-x-auto min-h-[49px]', left: 'flex items-center gap-1.5', right: 'flex items-center gap-1.5' } } } }) ``` # Drawer ## Usage Use a [Button](https://ui.nuxt.com/components/button) or any other component in the default slot of the Drawer. Then, use the `#content` slot to add the content displayed when the Drawer is open. ```vue ``` You can also use the `#header`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}, `#body`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} and `#footer`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} slots to customize the Drawer's content. ### Title Use the `title` prop to set the title of the Drawer's header. ```vue ``` ### Description Use the `description` prop to set the description of the Drawer's header. ```vue ``` ### Direction Use the `direction` prop to control the direction of the Drawer. Defaults to `bottom`. ```vue ``` ### Inset Use the `inset` prop to inset the Drawer from the edges. ```vue ``` ### Handle Use the `handle` prop to control whether the Drawer has a handle or not. Defaults to `true`. ```vue ``` ### Handle Only Use the `handle-only` prop to only allow the Drawer to be dragged by the handle. ```vue ``` ### Overlay Use the `overlay` prop to control whether the Drawer has an overlay or not. Defaults to `true`. ```vue ``` ### Scale background Use the `should-scale-background` prop to scale the background when the Drawer is open, creating a visual depth effect. You can set the `set-background-color-on-scale` prop to `false` to prevent changing the background color. ```vue ``` ::warning Make sure to add the `data-vaul-drawer-wrapper` directive to a parent element of your app to make this work. ```vue [app.vue] ``` ```ts [nuxt.config.ts] export default defineNuxtConfig({ app: { rootAttrs: { 'data-vaul-drawer-wrapper': '', 'class': 'bg-(--ui-bg)' } } }) ``` :: ## Examples ### Control open state You can control the open state by using the `default-open` prop or the `v-model:open` directive. ```vue [DrawerOpenExample.vue] ``` ::note In this example, leveraging [`defineShortcuts`](https://ui.nuxt.com/composables/define-shortcuts), you can toggle the Drawer by pressing ``. :: ::tip This allows you to move the trigger outside of the Drawer or remove it entirely. :: ### Prevent closing Set the `dismissible` prop to `false` to prevent the Drawer from being closed when clicking outside of it or pressing escape. ```vue [DrawerDismissibleExample.vue] ``` ::note In this example, the `header` slot is used to add a close button which is not done by default. :: ### Responsive drawer You can render a [Modal](https://ui.nuxt.com/components/modal) component on desktop and a Drawer on mobile for example. ```vue [DrawerResponsiveExample.vue] ``` ### With footer slot Use the `#footer` slot to add content after the Drawer's body. ```vue [DrawerFooterSlotExample.vue] ``` ### With command palette You can use a [CommandPalette](https://ui.nuxt.com/components/command-palette) component inside the Drawer's content. ```vue [DrawerCommandPaletteExample.vue] ``` ## API ### Props ```ts /** * Props for the Drawer component */ interface DrawerProps { /** * The element or component this component should render as. */ as?: any; title?: string | undefined; description?: string | undefined; /** * Whether to inset the drawer from the edges. */ inset?: boolean | undefined; /** * The content of the drawer. */ content?: (Omit & Partial>) | undefined; /** * Render an overlay behind the drawer. * @default "true" */ overlay?: boolean | undefined; /** * Render a handle on the drawer. * @default "true" */ handle?: boolean | undefined; /** * Render the drawer in a portal. * @default "true" */ portal?: boolean | undefined; ui?: { overlay?: ClassNameValue; content?: ClassNameValue; handle?: ClassNameValue; container?: ClassNameValue; header?: ClassNameValue; title?: ClassNameValue; description?: ClassNameValue; body?: ClassNameValue; footer?: ClassNameValue; } | undefined; /** * When `true`, don't move the drawer upwards if there's space, but rather only change it's height so it's fully scrollable when the keyboard is open */ fixed?: boolean | undefined; open?: boolean | undefined; /** * Opened by default, skips initial enter animation. Still reacts to `open` state changes */ defaultOpen?: boolean | undefined; activeSnapPoint?: string | number | null | undefined; /** * Number between 0 and 1 that determines when the drawer should be closed. * Example: threshold of 0.5 would close the drawer if the user swiped for 50% of the height of the drawer or more. */ closeThreshold?: number | undefined; shouldScaleBackground?: boolean | undefined; /** * When `false` we don't change body's background color when the drawer is open. */ setBackgroundColorOnScale?: boolean | undefined; /** * Duration for which the drawer is not draggable after scrolling content inside of the drawer. */ scrollLockTimeout?: number | undefined; /** * When `false` dragging, clicking outside, pressing esc, etc. will not close the drawer. * Use this in combination with the `open` prop, otherwise you won't be able to open/close the drawer. * @default "true" */ dismissible?: boolean | undefined; /** * When `false` it allows to interact with elements outside of the drawer without closing it. * @default "true" */ modal?: boolean | undefined; nested?: boolean | undefined; /** * Direction of the drawer. Can be `top` or `bottom`, `left`, `right`. * @default "\"bottom\"" */ direction?: DrawerDirection | undefined; /** * When `true` the `body` doesn't get any styles assigned from Vaul */ noBodyStyles?: boolean | undefined; /** * When `true` only allows the drawer to be dragged by the `` component. */ handleOnly?: boolean | undefined; preventScrollRestoration?: boolean | undefined; /** * Array of numbers from 0 to 100 that corresponds to % of the screen a given snap point should take up. * Should go from least visible. Example `[0.2, 0.5, 0.8]`. * You can also use px values, which doesn't take screen height into account. */ snapPoints?: (string | number)[] | undefined; } ``` ### Slots ```ts /** * Slots for the Drawer component */ interface DrawerSlots { default(): any; content(): any; header(): any; title(): any; description(): any; body(): any; footer(): any; } ``` ### Emits ```ts /** * Emitted events for the Drawer component */ interface DrawerEmits { close: (payload: []) => void; drag: (payload: [percentageDragged: number]) => void; release: (payload: [open: boolean]) => void; update:open: (payload: [open: boolean]) => void; update:activeSnapPoint: (payload: [val: string | number]) => void; animationEnd: (payload: [open: boolean]) => void; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ ui: { drawer: { slots: { overlay: 'fixed inset-0 bg-(--ui-bg-elevated)/75', content: 'fixed bg-(--ui-bg) ring ring-(--ui-border) flex focus:outline-none', handle: [ 'shrink-0 !bg-(--ui-bg-accented)', 'transition-opacity' ], container: 'w-full flex flex-col gap-4 p-4 overflow-y-auto', header: '', title: 'text-(--ui-text-highlighted) font-semibold', description: 'mt-1 text-(--ui-text-muted) text-sm', body: 'flex-1', footer: 'flex flex-col gap-1.5' }, variants: { direction: { top: { content: 'mb-24 flex-col-reverse', handle: 'mb-4' }, right: { content: 'flex-row', handle: '!ml-4' }, bottom: { content: 'mt-24 flex-col', handle: 'mt-4' }, left: { content: 'flex-row-reverse', handle: '!mr-4' } }, inset: { true: { content: 'rounded-lg after:hidden' } } }, compoundVariants: [ { direction: [ 'top', 'bottom' ], class: { content: 'h-auto max-h-[96%]', handle: '!w-12 !h-1.5 mx-auto' } }, { direction: [ 'right', 'left' ], class: { content: 'w-auto max-w-[calc(100%-2rem)]', handle: '!h-12 !w-1.5 mt-auto mb-auto' } }, { direction: 'top', inset: true, class: { content: 'inset-x-4 top-4' } }, { direction: 'top', inset: false, class: { content: 'inset-x-0 top-0 rounded-b-lg' } }, { direction: 'bottom', inset: true, class: { content: 'inset-x-4 bottom-4' } }, { direction: 'bottom', inset: false, class: { content: 'inset-x-0 bottom-0 rounded-t-lg' } }, { direction: 'left', inset: true, class: { content: 'inset-y-4 left-4' } }, { direction: 'left', inset: false, class: { content: 'inset-y-0 left-0 rounded-r-lg' } }, { direction: 'right', inset: true, class: { content: 'inset-y-4 right-4' } }, { direction: 'right', inset: false, class: { content: 'inset-y-0 right-0 rounded-l-lg' } } ] } } }) ``` # DropdownMenu ## Usage Use a [Button](https://ui.nuxt.com/components/button) or any other component in the default slot of the DropdownMenu. ### Items Use the `items` prop as an array of objects with the following properties: - `label?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `icon?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `color?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `avatar?: AvatarProps`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `kbds?: string[] | KbdProps[]`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - [`type?: "link" | "label" | "separator" | "checkbox"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-checkbox-items) - [`color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-color-items) - [`checked?: boolean`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-checkbox-items) - `disabled?: boolean`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `class?: any`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - [`slot?: string`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-custom-slot) - `onSelect?(e: Event): void`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - [`onUpdateChecked?(checked: boolean): void`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"}](https://ui.nuxt.com/#with-checkbox-items) - `children?: DropdownMenuItem[] | DropdownMenuItem[][]`{className="language-ts-type 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/components/link#props) component such as `to`, `target`, etc. ```vue ``` ::note You can also pass an array of arrays to the `items` prop to create separated groups of items. :: ::tip Each item can take a `children` array of objects with the same properties as the `items` prop to create a nested menu which can be controlled using the `open`, `defaultOpen` and `content` properties. :: ### Content Use the `content` prop to control how the DropdownMenu content is rendered, like its `align` or `side` for example. ```vue ``` ### Arrow Use the `arrow` prop to display an arrow on the DropdownMenu. ```vue ``` ### Size Use the `size` prop to control the size of the DropdownMenu. ```vue ``` ::warning The `size` prop will not be proxied to the Button, you need to set it yourself. :: ::note When using the same size, the DropdownMenu items will be perfectly aligned with the Button. :: ### Disabled Use the `disabled` prop to disable the DropdownMenu. ```vue ``` ## Examples ### With checkbox items You can use the `type` property with `checkbox` and use the `checked` / `onUpdateChecked` properties to control the checked state of the item. ```vue [DropdownMenuCheckboxItemsExample.vue] ``` ::note To ensure reactivity for the `checked` state of items, it's recommended to wrap your `items` array inside a `computed`. :: ### With color items You can use the `color` property to highlight certain items with a color. ```vue [DropdownMenuColorItemsExample.vue] ``` ### Control open state You can control the open state by using the `default-open` prop or the `v-model:open` directive. ```vue [DropdownMenuOpenExample.vue] ``` ::note In this example, leveraging [`defineShortcuts`](https://ui.nuxt.com/composables/define-shortcuts), you can toggle the DropdownMenu by pressing ``. :: ### With custom slot Use the `slot` property to customize a specific item. You will have access to the following slots: - `#{{ item.slot }}`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-leading`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-label`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} - `#{{ item.slot }}-trailing`{className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" lang="ts-type"} ```vue [DropdownMenuCustomSlotExample.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. :: ### Extract shortcuts When you have some items with `kbds` property (displaying some [Kbd](https://ui.nuxt.com/components/kbd)), you can easily make them work with the [defineShortcuts](https://ui.nuxt.com/composables/define-shortcuts) composable. Inside the `defineShortcuts` composable, there is an `extractShortcuts` utility that will extract the shortcuts recursively from the items and return an object that you can pass to `defineShortcuts`. It will automatically call the `select` function of the item when the shortcut is pressed. ```vue ``` ::note In this example, `` ``, `` `` and `` `` would trigger the `select` function of the corresponding item. :: ## API ### Props ```ts /** * Props for the DropdownMenu component */ interface DropdownMenuProps { size?: "sm" | "md" | "xs" | "lg" | "xl" | undefined; items?: ArrayOrNested | undefined; /** * The icon displayed when an item is checked. */ checkedIcon?: string | undefined; /** * The icon displayed when an item is loading. */ loadingIcon?: string | undefined; /** * The icon displayed when the item is an external link. * Set to `false` to hide the external icon. * @default "true" */ externalIcon?: string | boolean | undefined; /** * The content of the menu. */ content?: (Omit & Partial>) | undefined; /** * Display an arrow alongside the menu. */ arrow?: boolean | Omit | undefined; /** * Render the menu in a portal. * @default "true" */ portal?: boolean | undefined; /** * The key used to get the label from the item. * @default "\"label\"" */ labelKey?: keyof DropdownMenuItem | undefined; disabled?: boolean | undefined; ui?: { content?: ClassNameValue; arrow?: ClassNameValue; group?: ClassNameValue; label?: ClassNameValue; separator?: ClassNameValue; ... 9 more ...; itemLabelExternalIcon?: ClassNameValue; } | undefined; /** * The open state of the dropdown menu when it is initially rendered. Use when you do not need to control its open state. */ defaultOpen?: boolean | undefined; /** * The controlled open state of the menu. Can be used as `v-model:open`. */ open?: boolean | undefined; /** * The modality of the dropdown menu. * * When set to `true`, interaction with outside elements will be disabled and only menu content will be visible to screen readers. * @default "true" */ modal?: boolean | undefined; } ``` ### Slots ```ts /** * Slots for the DropdownMenu component */ interface DropdownMenuSlots { default(): any; item(): any; item-leading(): any; item-label(): any; item-trailing(): any; } ``` ### Emits ```ts /** * Emitted events for the DropdownMenu component */ interface DropdownMenuEmits { update:open: (payload: [payload: boolean]) => void; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ ui: { dropdownMenu: { slots: { content: 'min-w-32 bg-(--ui-bg) shadow-lg rounded-md ring ring-(--ui-border) divide-y divide-(--ui-border) overflow-y-auto scroll-py-1 data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in]', arrow: 'fill-(--ui-border)', group: 'p-1 isolate', label: 'w-full flex items-center font-semibold text-(--ui-text-highlighted)', separator: '-mx-1 my-1 h-px bg-(--ui-border)', item: 'group relative w-full flex items-center select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75', itemLeadingIcon: 'shrink-0', itemLeadingAvatar: 'shrink-0', itemLeadingAvatarSize: '', itemTrailing: 'ms-auto inline-flex gap-1.5 items-center', itemTrailingIcon: 'shrink-0', itemTrailingKbds: 'hidden lg:inline-flex items-center shrink-0', itemTrailingKbdsSize: '', itemLabel: 'truncate', itemLabelExternalIcon: 'inline-block size-3 align-top text-(--ui-text-dimmed)' }, variants: { color: { primary: '', secondary: '', success: '', info: '', warning: '', error: '', neutral: '' }, active: { true: { item: 'text-(--ui-text-highlighted) before:bg-(--ui-bg-elevated)', itemLeadingIcon: 'text-(--ui-text)' }, false: { item: [ 'text-(--ui-text) data-highlighted:text-(--ui-text-highlighted) data-[state=open]:text-(--ui-text-highlighted) data-highlighted:before:bg-(--ui-bg-elevated)/50 data-[state=open]:before:bg-(--ui-bg-elevated)/50', 'transition-colors before:transition-colors' ], itemLeadingIcon: [ 'text-(--ui-text-dimmed) group-data-highlighted:text-(--ui-text) group-data-[state=open]:text-(--ui-text)', 'transition-colors' ] } }, loading: { true: { itemLeadingIcon: 'animate-spin' } }, size: { xs: { label: 'p-1 text-xs gap-1', item: 'p-1 text-xs gap-1', itemLeadingIcon: 'size-4', itemLeadingAvatarSize: '3xs', itemTrailingIcon: 'size-4', itemTrailingKbds: 'gap-0.5', itemTrailingKbdsSize: 'sm' }, sm: { label: 'p-1.5 text-xs gap-1.5', item: 'p-1.5 text-xs gap-1.5', itemLeadingIcon: 'size-4', itemLeadingAvatarSize: '3xs', itemTrailingIcon: 'size-4', itemTrailingKbds: 'gap-0.5', itemTrailingKbdsSize: 'sm' }, md: { label: 'p-1.5 text-sm gap-1.5', item: 'p-1.5 text-sm gap-1.5', itemLeadingIcon: 'size-5', itemLeadingAvatarSize: '2xs', itemTrailingIcon: 'size-5', itemTrailingKbds: 'gap-0.5', itemTrailingKbdsSize: 'md' }, lg: { label: 'p-2 text-sm gap-2', item: 'p-2 text-sm gap-2', itemLeadingIcon: 'size-5', itemLeadingAvatarSize: '2xs', itemTrailingIcon: 'size-5', itemTrailingKbds: 'gap-1', itemTrailingKbdsSize: 'md' }, xl: { label: 'p-2 text-base gap-2', item: 'p-2 text-base gap-2', itemLeadingIcon: 'size-6', itemLeadingAvatarSize: 'xs', itemTrailingIcon: 'size-6', itemTrailingKbds: 'gap-1', itemTrailingKbdsSize: 'lg' } } }, compoundVariants: [ { color: 'primary', active: false, class: { item: 'text-(--ui-primary) data-highlighted:text-(--ui-primary) data-highlighted:before:bg-(--ui-primary)/10 data-[state=open]:before:bg-(--ui-primary)/10', itemLeadingIcon: 'text-(--ui-primary)/75 group-data-highlighted:text-(--ui-primary) group-data-[state=open]:text-(--ui-primary)' } }, { color: 'secondary', active: false, class: { item: 'text-(--ui-secondary) data-highlighted:text-(--ui-secondary) data-highlighted:before:bg-(--ui-secondary)/10 data-[state=open]:before:bg-(--ui-secondary)/10', itemLeadingIcon: 'text-(--ui-secondary)/75 group-data-highlighted:text-(--ui-secondary) group-data-[state=open]:text-(--ui-secondary)' } }, { color: 'success', active: false, class: { item: 'text-(--ui-success) data-highlighted:text-(--ui-success) data-highlighted:before:bg-(--ui-success)/10 data-[state=open]:before:bg-(--ui-success)/10', itemLeadingIcon: 'text-(--ui-success)/75 group-data-highlighted:text-(--ui-success) group-data-[state=open]:text-(--ui-success)' } }, { color: 'info', active: false, class: { item: 'text-(--ui-info) data-highlighted:text-(--ui-info) data-highlighted:before:bg-(--ui-info)/10 data-[state=open]:before:bg-(--ui-info)/10', itemLeadingIcon: 'text-(--ui-info)/75 group-data-highlighted:text-(--ui-info) group-data-[state=open]:text-(--ui-info)' } }, { color: 'warning', active: false, class: { item: 'text-(--ui-warning) data-highlighted:text-(--ui-warning) data-highlighted:before:bg-(--ui-warning)/10 data-[state=open]:before:bg-(--ui-warning)/10', itemLeadingIcon: 'text-(--ui-warning)/75 group-data-highlighted:text-(--ui-warning) group-data-[state=open]:text-(--ui-warning)' } }, { color: 'error', active: false, class: { item: 'text-(--ui-error) data-highlighted:text-(--ui-error) data-highlighted:before:bg-(--ui-error)/10 data-[state=open]:before:bg-(--ui-error)/10', itemLeadingIcon: 'text-(--ui-error)/75 group-data-highlighted:text-(--ui-error) group-data-[state=open]:text-(--ui-error)' } }, { color: 'primary', active: true, class: { item: 'text-(--ui-primary) before:bg-(--ui-primary)/10', itemLeadingIcon: 'text-(--ui-primary)' } }, { color: 'secondary', active: true, class: { item: 'text-(--ui-secondary) before:bg-(--ui-secondary)/10', itemLeadingIcon: 'text-(--ui-secondary)' } }, { color: 'success', active: true, class: { item: 'text-(--ui-success) before:bg-(--ui-success)/10', itemLeadingIcon: 'text-(--ui-success)' } }, { color: 'info', active: true, class: { item: 'text-(--ui-info) before:bg-(--ui-info)/10', itemLeadingIcon: 'text-(--ui-info)' } }, { color: 'warning', active: true, class: { item: 'text-(--ui-warning) before:bg-(--ui-warning)/10', itemLeadingIcon: 'text-(--ui-warning)' } }, { color: 'error', active: true, class: { item: 'text-(--ui-error) before:bg-(--ui-error)/10', itemLeadingIcon: 'text-(--ui-error)' } } ], defaultVariants: { size: 'md' } } } }) ``` # Error ## Usage Like the [Main](https://ui.nuxt.com/components/main) component, the Error component renders a `
` element that works together with the [Header](https://ui.nuxt.com/components/header) component to create a full-height layout that extends to the viewport's available height. ::note The Header component defines its height through a `--ui-header-height` CSS variable, which you can customize by overriding it in your CSS: ```css :root { --ui-header-height: --spacing(16); } ``` :: ### Error Use the `error` prop to display an error message. ::note --- target: _blank to: https://nuxt.com/docs/guide/directory-structure/error --- In most cases, you will receive the `error` prop in your `error.vue` file. :: ```vue ``` ### Clear Use the `clear` prop to customize or hide the clear button (with `false` value). You can pass any property from the [Button](https://ui.nuxt.com/components/button) component to customize it. ```vue ``` ### Redirect Use the `redirect` prop to redirect the user to a different page when the clear button is clicked. Defaults to `/`. ```vue ``` ## Examples ### Within `error.vue` Use the Error component in your `error.vue`: ```vue [error.vue] {13} ``` ::tip You might want to replicate the code of your `app.vue` inside your `error.vue` file to have the same layout and features, here is an example: {rel="nofollow"} :: ::note You can read more about how to handle errors in the [Nuxt documentation](https://nuxt.com/docs/getting-started/error-handling#error-page){rel="nofollow"}, but when using `nuxt generate` it is recommended to add `fatal: true` inside your `createError` call to make sure the error page is displayed: ```vue [pages/[...slug\\].vue] ``` :: ## API ### Props ```ts /** * Props for the Error component */ interface ErrorProps { /** * @default "true" */ clear?: boolean | Partial | undefined; /** * @default "\"main\"" */ as?: any; /** * @default "\"/\"" */ redirect?: string | undefined; error?: Partial & { message: string; }> | undefined; ui?: { root?: ClassNameValue; statusCode?: ClassNameValue; statusMessage?: ClassNameValue; message?: ClassNameValue; links?: ClassNameValue; } | undefined; } ``` ### Slots ```ts /** * Slots for the Error component */ interface ErrorSlots { default(): any; statusCode(): any; statusMessage(): any; links(): any; } ``` ## Theme ```ts [app.config.ts] export default defineAppConfig({ uiPro: { error: { slots: { root: 'min-h-[calc(100vh-var(--ui-header-height))] flex flex-col items-center justify-center text-center', statusCode: 'text-base font-semibold text-(--ui-primary)', statusMessage: 'mt-2 text-4xl sm:text-5xl font-bold text-(--ui-text-highlighted) text-balance', message: 'mt-4 text-lg text-(--ui-text-muted) text-balance', links: 'mt-8 flex items-center justify-center gap-6' } } } }) ``` # Footer ## Usage The Footer component renders a `