# Conventions Coding patterns specific to Nuxt UI. ## Auto-registered modules Nuxt UI automatically registers `@nuxt/icon`, `@nuxt/fonts`, and `@nuxtjs/color-mode`. Do **not** add them to your `modules` array. Configure them via root-level keys in `nuxt.config.ts`: ```ts // nuxt.config.ts export default defineNuxtConfig({ modules: ['@nuxt/ui'], icon: { /* @nuxt/icon options */ }, fonts: { /* @nuxt/fonts options */ }, colorMode: { /* @nuxtjs/color-mode options */ } }) ``` Disable any of them: `ui: { fonts: false }`, `ui: { colorMode: false }`. ## Content module integration When using `@nuxt/content`, it **must** come after `@nuxt/ui` in the `modules` array — otherwise prose components won't be available: ```ts // nuxt.config.ts export default defineNuxtConfig({ modules: ['@nuxt/ui', '@nuxt/content'] }) ``` Add `@source` in your CSS so Tailwind generates classes used in markdown/MDC: ```css /* app/assets/css/main.css */ @import "tailwindcss"; @import "@nuxt/ui"; @source "../../../content/**/*"; ``` Use `mapContentNavigation` to transform content navigation for components like `UBreadcrumb`: ```ts import { mapContentNavigation } from '@nuxt/ui/utils/content' import { findPageBreadcrumb } from '@nuxt/content/utils' const breadcrumb = computed(() => mapContentNavigation(findPageBreadcrumb(navigation.value, page.value?.path)) ) ``` ## IDE setup Recommended `.vscode/settings.json` for Tailwind IntelliSense autocomplete with Nuxt UI: ```json { "files.associations": { "*.css": "tailwindcss" }, "editor.quickSuggestions": { "strings": "on" }, "tailwindCSS.classAttributes": ["class", "ui"], "tailwindCSS.classFunctions": ["defineAppConfig"] } ``` ## UApp wrapper Always wrap your app in `UApp` — it provides: - Toast container (`useToast`) - Tooltip provider - Programmatic overlay context (`useOverlay`) - i18n locale support ```vue ``` ## Icons Nuxt UI registers `@nuxt/icon` automatically. Format: `i-{collection}-{name}`. Prefer `lucide` collection. ```vue ``` Install collections locally for reliable SSR and best performance: ```bash pnpm i @iconify-json/lucide pnpm i @iconify-json/simple-icons ``` Custom local collections (Nuxt only): ```ts // nuxt.config.ts export default defineNuxtConfig({ icon: { customCollections: [{ prefix: 'custom', dir: './app/assets/icons' }] } }) ``` ### Default icon overrides Components like `Modal`, `Select`, `Accordion`, etc. use default icons from `appConfig.ui.icons`. Override them globally: ```ts // app.config.ts export default defineAppConfig({ ui: { icons: { loading: 'i-lucide-refresh-cw', close: 'i-lucide-x', check: 'i-lucide-check', chevronDown: 'i-lucide-chevron-down', chevronRight: 'i-lucide-chevron-right', arrowLeft: 'i-lucide-arrow-left', arrowRight: 'i-lucide-arrow-right' } } }) ``` ## Slot patterns Most components follow consistent slot naming: | Slot | Used by | Purpose | | ----------- | -------------------------------------- | ------------------------------- | | `#header` | Card, Modal, Slideover, DashboardPanel | Top section | | `#body` | DashboardPanel | Scrollable content area | | `#footer` | Card, Modal, Slideover, DashboardPanel | Bottom section | | `#left` | Page, DashboardNavbar | Left sidebar or content | | `#right` | Page, DashboardNavbar, Header | Right sidebar or content | | `#leading` | Input, Button, Alert | Before main content (icon area) | | `#trailing` | Input, Button | After main content (icon area) | | `#content` | Modal, Slideover, Popover, Tooltip | Full content override | | `#default` | Most components | Main content area | ## Items arrays Many components accept an `items` prop. Two patterns: **Flat array** — plain list: ```ts const items = [ { label: 'Edit', icon: 'i-lucide-pencil' }, { label: 'Delete', icon: 'i-lucide-trash', color: 'error' } ] ``` **Nested array** — groups with automatic separators between them: ```ts const items = [ [ { label: 'Edit', icon: 'i-lucide-pencil' }, { label: 'Duplicate', icon: 'i-lucide-copy' } ], [ { label: 'Delete', icon: 'i-lucide-trash', color: 'error' } ] ] ``` Components supporting nested arrays: `UDropdownMenu`, `UContextMenu`, `UNavigationMenu`. `UCommandPalette` uses a `groups` prop instead, with an `items` array on each group. ## Composables ### useToast ```ts const toast = useToast() toast.add({ title: 'Success', description: 'Item saved', color: 'success', icon: 'i-lucide-check-circle', duration: 5000, actions: [{ label: 'Undo', onClick: () => {} }] }) toast.remove('toast-id') toast.clear() ``` ### useOverlay Programmatic modals, slideovers, drawers — no template `v-model` needed. See [overlays recipe](../recipes/overlays.md) for full patterns. ```ts const overlay = useOverlay() const modal = overlay.create(MyComponent) const instance = modal.open({ title: 'Confirm?' }) if (await instance.result) { /* confirmed */ } ``` ### defineShortcuts ```ts defineShortcuts({ meta_k: () => openSearch(), escape: () => close(), meta_enter: { handler: () => submit(), whenever: [isFormValid] } }) ``` Keys: `meta` (Cmd/Ctrl), `ctrl`, `alt`, `shift`. Separator: `_`. ### extractShortcuts Wire up keyboard shortcuts from menu items: ```ts const items = [ { label: 'New file', kbds: ['meta', 'n'], onSelect: () => newFile() }, { label: 'Save', kbds: ['meta', 's'], onSelect: () => save() } ] defineShortcuts(extractShortcuts(items)) ``` ### Internationalization (i18n) Nuxt UI supports 50+ locales. Set the locale on `UApp` — all components inherit it. #### Static locale ```vue ``` #### Extend a built-in locale `extendLocale` is auto-imported. Override specific messages or the `code` (affects date/time formatting in Calendar, InputDate, InputTime): ```ts import { en } from '@nuxt/ui/locale' const locale = extendLocale(en, { code: 'en-AU', messages: { commandPalette: { placeholder: 'Search a component...' } } }) ``` #### Custom locale from scratch ```ts import type { Messages } from '@nuxt/ui' const locale = defineLocale({ name: 'My locale', code: 'en', dir: 'ltr', messages: { // all component message keys } }) ``` #### Dynamic locale with @nuxtjs/i18n ```ts // nuxt.config.ts export default defineNuxtConfig({ modules: ['@nuxt/ui', '@nuxtjs/i18n'], i18n: { locales: [ { code: 'en', name: 'English' }, { code: 'fr', name: 'Français' }, { code: 'ar', name: 'العربية' } ] } }) ``` ```vue ``` Each locale has a `dir` property (`'ltr'` or `'rtl'`). `UApp` uses it to set directionality on all components. Use `useHead` to propagate `lang` and `dir` to the `` element. ## Color mode Nuxt UI registers `@nuxtjs/color-mode` automatically. Built-in components for switching: - `UColorModeButton` — single button toggle (light/dark) - `UColorModeSwitch` — toggle switch - `UColorModeSelect` — dropdown with system/light/dark options - `UColorModeAvatar` — displays different avatar per mode - `UColorModeImage` — displays different image per mode For custom color mode UI, use `useColorMode` with `ClientOnly` to avoid hydration mismatch: ```vue ``` ## Official templates Bootstrap a project from a template instead of starting from scratch: ```bash npx nuxi@latest init -t ui # Starter npx nuxi@latest init -t ui/dashboard # Dashboard npx nuxi@latest init -t ui/docs # Docs (Nuxt Content) npx nuxi@latest init -t ui/landing # Landing page npx nuxi@latest init -t ui/saas # SaaS (landing + pricing + docs + blog) npx nuxi@latest init -t ui/chat # AI chat (Vercel AI SDK) npx nuxi@latest init -t ui/editor # Rich text editor npx nuxi@latest init -t ui/portfolio # Portfolio npx nuxi@latest init -t ui/changelog # Changelog npx nuxi@latest init -t ui/calendar # Calendar ``` ## Responsive patterns - Dashboard sidebar hides on mobile, shows a slideover/drawer via `UDashboardSidebar` `mode` prop - `UHeader` body slot is the mobile menu content (shown when hamburger is tapped) - Most components handle responsiveness automatically — avoid manual breakpoint classes unless needed - Use `UPageAside` for sidebars that should hide below `lg` breakpoint