# @vuetify/v0 - Complete Documentation > Headless Vue 3 UI primitives and composables for building modern applications and design systems. --- # Contributing URL: https://0.vuetifyjs.com/introduction/contributing # Contributing Thank you for your interest in contributing to Vuetify0! This guide will help you get started. ## Getting Started Before contributing, please: 1. Read the [Getting Started](/introduction/getting-started) guide to understand the project 2. Review existing [issues](https://github.com/vuetifyjs/0/issues) 3. Join our [Discord community](https://community.vuetifyjs.com) for questions ## Reporting Issues ### Bug Reports When reporting bugs, please include: - A clear, descriptive title - Steps to reproduce the issue - Expected vs actual behavior - Browser and OS information - A minimal reproduction (preferably a link to a repo or CodeSandbox) ### Feature Requests For new features: - Check if it's already been requested in [issues](https://github.com/vuetifyjs/0/issues) - Explain the use case and why it would benefit others - Consider if it fits the headless/composable philosophy of Vuetify0 ## Local Development ### Prerequisites - Node.js 20.19+ or 22+ - pnpm 10.6+ - Git ### Setup ```bash # Clone the repository git clone https://github.com/vuetifyjs/0.git cd 0 # Install dependencies pnpm install # Start the playground pnpm dev # Start the docs site pnpm dev:docs ``` ### Project Structure ```text ├── packages/ │ └── 0/ # @vuetify/v0 - main package │ ├── src/ │ │ ├── components/ # Vue components │ │ ├── composables/ # Composable functions │ │ ├── utilities/ # Helper functions │ │ └── types/ # TypeScript types ├── apps/ │ ├── docs/ # Documentation site │ └── storybook/ # Storybook stories └── playground/ # Development playground ``` ### Useful Commands ```bash # Development pnpm dev # Start playground pnpm dev:docs # Start docs site # Testing pnpm test # Run tests in watch mode pnpm test:run # Run tests once # Type checking pnpm typecheck # Check all packages # Linting pnpm lint # Lint codebase pnpm lint:fix # Auto-fix lint issues # Building pnpm build # Build packages ``` ## Pull Requests ### Before Submitting 1. Create a new branch from `master` 2. Make your changes 3. Run `pnpm lint:fix` to fix formatting 4. Run `pnpm typecheck` to check types 5. Run `pnpm test:run` to verify tests pass 6. Write tests for new functionality ### PR Guidelines - Keep PRs focused - one feature or fix per PR - Write a clear title and description - Reference any related issues - Be responsive to feedback ### Branch Naming Use descriptive branch names: - `fix/issue-description` - Bug fixes - `feat/feature-name` - New features - `docs/what-changed` - Documentation updates - `refactor/what-changed` - Code refactoring ## Commit Messages Follow the [Conventional Commits](https://www.conventionalcommits.org/) format: ```text type(scope): subject ``` ### Types - `feat` - New feature - `fix` - Bug fix - `docs` - Documentation changes - `refactor` - Code refactoring - `test` - Adding or updating tests - `chore` - Maintenance tasks ### Examples ```bash feat(useSelection): add toggle method fix(ExpansionPanel): correct aria-expanded state docs(getting-started): update installation instructions refactor(useRegistry): simplify reindex logic test(useForm): add validation edge cases ``` ### Guidelines - Use imperative mood ("add" not "added") - Keep the subject under 60 characters - Don't end with a period - Reference issues when applicable: `fix(useForm): validation error (#123)` ## Code Style ### General - Use TypeScript for all new code - Follow existing patterns in the codebase - Prefer `function` declarations over `const` arrow functions - Use single-word variable names when clear - Add JSDoc comments for public APIs ### Composables - Place in `packages/0/src/composables/` - Each composable in its own directory with `index.ts` - Include `@module` JSDoc block at the top - Colocate tests as `index.test.ts` - Export both standalone and context-creation functions ### Components - Follow the compound component pattern (Root/Item) - Extend `AtomProps` for polymorphic components - Use `useProxyModel` for v-model binding - Include proper ARIA attributes ## Testing - Write tests for new composables and components - Focus on behavior, not implementation details - Test edge cases and error conditions - Use `describe` blocks to organize related tests ```ts describe('useSelection', () => { it('should select an item', () => { // ... }) it('should respect mandatory option', () => { // ... }) }) ``` ## Questions? - [Discord](https://community.vuetifyjs.com) - Real-time chat and questions - [GitHub Issues](https://github.com/vuetifyjs/0/issues) - Bug reports and feature requests Thank you for contributing! --- # Frequently Asked URL: https://0.vuetifyjs.com/introduction/frequently-asked # Frequently Asked Common questions and answers about Vuetify0. Have a question that isn't answered here? Join our [Discord community](https://community.vuetifyjs.com) or open an [issue on GitHub](https://github.com/vuetifyjs/0/issues). --- # Getting Started URL: https://0.vuetifyjs.com/introduction/getting-started # Get started with Vuetify0 Vuetify0 provides headless UI primitives and composables for Vue 3. Components are unstyled and logic-focused, giving you complete control over styling while handling accessibility, keyboard navigation, and state management. ## Installation Install `@vuetify/v0` with your preferred package manager: ::: code-group ```bash pnpm pnpm add @vuetify/v0 ``` ```bash npm npm install @vuetify/v0 ``` ```bash yarn yarn add @vuetify/v0 ``` ```bash bun bun add @vuetify/v0 ``` ::: ## Requirements - Vue 3.3.0 or higher - Node 22+ ## Quick Start Import and use components directly - no plugin installation required: ```vue QuickStart.vue playground Section 1 Content for section 1 ``` Components are completely unstyled. Add your own classes using Tailwind, UnoCSS, or plain CSS. ## Styling v0 is style-agnostic. Choose your preferred CSS framework and map theme colors to v0's CSS variables. ### UnoCSS [UnoCSS](https://unocss.dev) is our recommended choice for its speed and flexibility. #### 1. Install ::: code-group ```bash pnpm pnpm add -D unocss @unocss/preset-wind ``` ```bash npm npm install -D unocss @unocss/preset-wind ``` ```bash yarn yarn add -D unocss @unocss/preset-wind ``` ```bash bun bun add -D unocss @unocss/preset-wind ``` ::: #### 2. Configure ```ts uno.config.ts import { defineConfig, presetWind } from 'unocss' export default defineConfig({ presets: [presetWind()], theme: { colors: { primary: 'var(--v0-primary)', surface: 'var(--v0-surface)', 'on-primary': 'var(--v0-on-primary)', 'on-surface': 'var(--v0-on-surface)', }, }, }) ``` #### 3. Add Vite Plugin ```ts vite.config.ts import UnoCSS from 'unocss/vite' export default defineConfig({ plugins: [ vue(), UnoCSS(), ], }) ``` #### 4. Import Styles ```ts main.ts import 'virtual:uno.css' ``` Now use utility classes in your components: ```vue Click me ``` ### Tailwind CSS v4 [Tailwind v4](https://tailwindcss.com) uses CSS-first configuration with native cascade layers. #### 1. Install ::: code-group ```bash pnpm pnpm add -D tailwindcss @tailwindcss/vite ``` ```bash npm npm install -D tailwindcss @tailwindcss/vite ``` ```bash yarn yarn add -D tailwindcss @tailwindcss/vite ``` ```bash bun bun add -D tailwindcss @tailwindcss/vite ``` ::: #### 2. Add Vite Plugin ```ts vite.config.ts import tailwindcss from '@tailwindcss/vite' export default defineConfig({ plugins: [ vue(), tailwindcss(), ], }) ``` #### 3. Create Stylesheet ```css src/styles/main.css @import "tailwindcss"; @theme { --color-primary: var(--v0-primary); --color-surface: var(--v0-surface); --color-on-primary: var(--v0-on-primary); --color-on-surface: var(--v0-on-surface); } ``` #### 4. Import Styles ```ts main.ts import './styles/main.css' ``` Now use utility classes in your components: ```vue Click me ``` ### CSS Modules Vue's built-in [CSS Modules](https://vuejs.org/api/sfc-css-features#css-modules) require zero configuration. ```vue Button.vue Click me ``` Type-safe access via `useCssModule()`: ```vue Click me ``` > [!TIP] > For dark mode, custom themes, and design tokens, see the [Theming Guide](/guide/theming). ## Nuxt 3 v0 works with Nuxt 3 via a standard plugin. ### 1. Create Plugin ```ts plugins/v0.ts import { createHydrationPlugin, createThemePlugin } from '@vuetify/v0' export default defineNuxtPlugin((nuxtApp) => { nuxtApp.vueApp.use(createHydrationPlugin()) nuxtApp.vueApp.use( createThemePlugin({ default: 'light', themes: { light: { dark: false, colors: { primary: '#3b82f6', surface: '#ffffff', 'on-primary': '#ffffff', 'on-surface': '#212121', }, }, dark: { dark: true, colors: { primary: '#60a5fa', surface: '#1e1e1e', 'on-primary': '#1a1a1a', 'on-surface': '#e0e0e0', }, }, }, }), ) }) ``` ### 2. Configure Nuxt ```ts nuxt.config.ts export default defineNuxtConfig({ build: { transpile: ['@vuetify/v0'], }, }) ``` > [!TIP] > For auto-imports, SSR hydration, and theme persistence, see the [Nuxt Guide](/guide/nuxt). ## Exposed Exports The following export paths exist for the Vuetify0 framework: | Name | Description | | ---- | ----------- | | `@vuetify/v0` | Main entry point exposing all components, composables, and utilities. | | `@vuetify/v0/components` | Components only. | | `@vuetify/v0/composables` | Composables only. | | `@vuetify/v0/utilities` | Utilities only. | | `@vuetify/v0/constants` | Constants only (not included in main entry). | ```ts // Everything import { ExpansionPanel, useSelection } from '@vuetify/v0' // Components only import { ExpansionPanel, Single, Group } from '@vuetify/v0/components' // Composables only import { useSelection, useTheme, useForm } from '@vuetify/v0/composables' // Utilities only import { isObject, isString } from '@vuetify/v0/utilities' // Constants only import { IN_BROWSER } from '@vuetify/v0/constants' ``` ## Next Steps - [Explore Components](/components/) See all available components - [Browse Composables](/composables/) Dive into the composables API --- # guide URL: https://0.vuetifyjs.com/guide # Guide Learn v0's patterns and build headless UI systems. Start with [Getting Started](/introduction/getting-started) if you haven't installed v0 yet. ## Prerequisites Before diving into the guides, ensure you're familiar with: - **Required**: Vue 3 basics (ref, computed, provide/inject) - **Helpful**: Composables pattern, TypeScript generics ## What's Different About v0 | Traditional Component Libraries | v0 Approach | | - | - | | Pre-styled, override with CSS | Zero styles - you own all styling | | Props configure behavior | Composables expose reactive state | | Component = black box | Component = transparent composition | | Fight the framework | Build with the framework | **The v0 Mental Model:** - Components are **delivery mechanisms**, not behavior containers - Logic lives in **composables** you can use independently - **Trinity pattern**: `[use, provide, context]` - predictable, debuggable - **Registry/Context pattern**: Parent-child coordination without prop drilling ## Learning Paths ### Track A: Core Concepts For understanding the system architecture. | Guide | What You'll Learn | | - | - | | [Structure](/guide/structure) | Package organization, imports, file conventions | | [Core](/guide/core) | Trinity, Context, Registry patterns | | [Components](/guide/components) | Component categories, Atom primitive, slot props | | [Plugins](/guide/plugins) | Using and creating Vue plugins | ### Track B: Features & Polish For building production UIs. | Guide | What You'll Learn | | - | - | | [Theming](/guide/theming) | CSS variables, design tokens, dark mode | | [Accessibility](/guide/accessibility) | ARIA patterns, keyboard nav, testing | | [Utilities](/guide/utilities) | Helper functions, type guards | > [!TIP] > New to v0? Start with Track A. Already building? Jump to Track B as needed. > [!SUGGESTION] How do I migrate an existing styled component library to use v0's headless approach? ## Quick Reference | Pattern | Use Case | Guide | | - | - | - | | `createContext` | Share state across component tree | [Core](/guide/core) | | `useSelection` | Multi-select, toggles, radio groups | [Composables](/composables/selection/use-selection) | | `useRegistry` | Dynamic child registration | [Core](/guide/core) | | `Atom` component | Polymorphic base element | [Components](/guide/components) | | `useTheme` | Theme switching, CSS variables | [Theming](/guide/theming) | --- # Accessibility URL: https://0.vuetifyjs.com/guide/accessibility # Accessibility v0 provides ARIA attributes out-of-the-box through the `attrs` pattern. You provide styling and visual feedback. ## The `attrs` Pattern Every v0 component exposes an `attrs` object containing all accessibility attributes. Spread it onto your elements: ```vue playground {{ item }} ``` ### What's Included in `attrs` | Component | ARIA Attributes Provided | | - | - | | Selection.Item | `aria-selected`, `aria-disabled`, `data-selected`, `data-disabled` | | Group.Item | `role="checkbox"`, `aria-checked`, `aria-disabled`, `data-selected`, `data-disabled`, `data-mixed` | | ExpansionPanel.Activator | `id`, `role`, `tabindex`, `aria-expanded`, `aria-controls`, `aria-disabled` | | Pagination.Root | `aria-label`, `role="navigation"` (when not using ``) | | Popover.Anchor | `popovertarget`, `data-popover-open` (uses native popover API) | > [!TIP] > Always spread the `attrs` object from slot props onto your interactive elements. Missing ARIA attributes break screen reader support. ## Developer Responsibilities v0 provides the ARIA plumbing. You must provide: | Responsibility | Example | | - | - | | Visual focus indicators | `:focus-visible { outline: 2px solid blue }` | | Color contrast | Ensure 4.5:1 ratio minimum | | Visible labels | Add `` or `aria-label` for inputs | | Skip links | Navigation landmarks for keyboard users | ### Focus Trapping v0 does **not** provide focus trapping. Use external solutions: - [focus-trap](https://github.com/focus-trap/focus-trap) - Native `inert` attribute for siblings - [vue-final-modal](https://vue-final-modal.org/) ### Roving Tabindex v0 does **not** provide roving tabindex. This keeps the library headless - implement in your design system layer if needed for arrow key navigation between items. ## Keyboard Navigation ### What v0 Handles - `tabindex` management (-1 when disabled, 0 when enabled) - ARIA state synchronization (`aria-expanded`, `aria-selected`) - Data attributes for styling (`data-selected`, `data-disabled`) ### What You Implement | Pattern | Keys to Handle | | - | - | | List selection | Arrow keys, Home/End | | Menus | Arrow keys, Escape, Enter | | Dialogs | Escape to close, focus trap | | Tabs | Arrow keys, Home/End | ```ts // You implement navigation logic - v0 provides selection state function onKeydown (e: KeyboardEvent, ids: string[], currentIndex: number) { switch (e.key) { case 'ArrowDown': selection.select(ids[currentIndex + 1]); break case 'ArrowUp': selection.select(ids[currentIndex - 1]); break case 'Home': selection.select(ids[0]); break case 'End': selection.select(ids[ids.length - 1]); break } } ``` ## Testing Strategies ### Automated Testing ```ts MyComponent.test.ts import { axe } from 'vitest-axe' it('passes accessibility audit', async () => { const { container } = render(MyComponent) expect(await axe(container)).toHaveNoViolations() }) ``` ### Manual Testing Checklist - [ ] Tab through all interactive elements - [ ] Verify focus visibility - [ ] Test with keyboard only (no mouse) - [ ] Check color contrast with DevTools - [ ] Validate with browser accessibility tree ### Recommended Tools | Tool | Purpose | | - | - | | axe DevTools | Browser extension for WCAG scanning | | Lighthouse | Built-in Chrome audit | | NVDA/VoiceOver | Screen reader verification | ## Internationalization v0's `useLocale` handles RTL and translated labels. See [useLocale](/composables/plugins/use-locale) for accessibility label translations. > [!SUGGESTION] How do I implement arrow key navigation for a custom list component? > [!SUGGESTION] How do I integrate accessibility testing into my CI/CD pipeline? --- # Ai Tools URL: https://0.vuetifyjs.com/guide/ai-tools # AI Tools v0 provides machine-readable documentation files following the [llms.txt](https://llmstxt.org/) standard. These files help AI assistants understand the library without hallucinating APIs or patterns. ## Available Files | File | Size | Purpose | Best For | | - | - | - | - | | llms.txt↗ | ~6 KB | Curated index with links | Quick context, navigation | | llms-full.txt↗ | ~220 KB | Complete documentation | Deep understanding, code generation | > [!SUGGESTION] When should I use llms.txt vs llms-full.txt? ## Usage Examples ### ChatGPT / Claude.ai Paste the URL directly in chat: ```txt Read https://0.vuetifyjs.com/llms-full.txt and help me build a multi-select dropdown using v0 composables. ``` ### Cursor / Windsurf Add to your project's `.cursorrules` or AI context: ```txt @https://0.vuetifyjs.com/llms.txt ``` ### Claude Code Fetch the documentation in your session: ```txt WebFetch https://0.vuetifyjs.com/llms-full.txt ``` > [!TIP] > For the best experience with Claude, use [Vuetify MCP](/guide/vuetify-mcp) instead. It provides structured API access rather than raw text. ## What's Included **llms.txt** contains categorized links to: - 10 guide pages (introduction, theming, accessibility, etc.) - 9 headless components (Atom, Avatar, Pagination, etc.) - 34 composables across 7 categories - FAQ and contributing guides **llms-full.txt** includes the complete content of every documentation page, stripped of Vue components and frontmatter for cleaner LLM consumption. ## How It Works - `llms.txt` is manually curated in `apps/docs/public/` - `llms-full.txt` is auto-generated at build time from all markdown pages - Both are served statically and cached for performance --- # Components URL: https://0.vuetifyjs.com/guide/components # Components v0 components are Vue wrappers around composables. Composables hold logic, components provide Vue integration via slots, props, and emits. ## Component Philosophy - **Headless**: Zero styling - you own all CSS - **Slot-driven**: All customization through scoped slots - **Accessible**: ARIA attributes via `attrs` object - **Composable-backed**: Use components or composables directly > [!TIP] > Always spread the `attrs` object onto your elements. It contains ARIA attributes and data attributes for accessibility and styling. > [!SUGGESTION] How do I add styling or CSS classes to headless components? ## Component Categories | Category | Purpose | Examples | | - | - | - | | **Primitives** | Base building blocks | [Atom](/components/primitives/atom) | | **Providers** | Pure state management, no DOM | [Selection](/components/providers/selection), [Single](/components/providers/single), [Group](/components/providers/group), [Step](/components/providers/step) | | **Semantic** | Meaningful HTML defaults | [Avatar](/components/semantic/avatar), [Pagination](/components/semantic/pagination) | | **Disclosure** | Show/hide patterns | [ExpansionPanel](/components/disclosure/expansion-panel), [Popover](/components/disclosure/popover) | ## Atom: The Foundation The `Atom` component is a polymorphic base element supporting any HTML tag: ```vue ``` ### Rendering Modes | Mode | Usage | Output | | - | - | - | | Element | `as="button"` | `` with slot content | | Renderless | `:as="null"` or `renderless` | Slot only, no wrapper | ## Slot Props Pattern Every component exposes `attrs` in its default slot. Spread onto your element for behavior and accessibility: ```vue playground {{ item }} ``` ### Common Slot Props | Component Type | Slot Props | | - | - | | Selection.Item | `attrs`, `isSelected`, `toggle`, `select`, `unselect` | | Group.Item | `attrs`, `isSelected`, `isMixed`, `toggle` | | ExpansionPanel.Activator | `attrs`, `isSelected`, `toggle` | | Popover.Root | `id`, `isSelected`, `toggle` | | Popover.Anchor | `attrs`, `isOpen` | | Popover.Content | `attrs`, `isOpen` | ### Data Attributes Components emit data attributes for CSS styling: ```css [data-selected] { background: var(--primary); } [data-disabled] { opacity: 0.5; } [data-mixed] { /* tri-state checkbox */ } [data-popover-open] { /* popover is visible */ } ``` ## Component Reference ### Primitives - [Atom](/components/primitives/atom) Polymorphic base element ### Providers - [Selection](/components/providers/selection) Multi-selection state - [Single](/components/providers/single) Single-selection state - [Group](/components/providers/group) Multi-select with tri-state - [Step](/components/providers/step) Sequential navigation ### Semantic - [Avatar](/components/semantic/avatar) Image with fallback - [Pagination](/components/semantic/pagination) Page navigation ### Disclosure - [ExpansionPanel](/components/disclosure/expansion-panel) Accordion pattern - [Popover](/components/disclosure/popover) Floating content --- # Composables URL: https://0.vuetifyjs.com/guide/composables # Composables Composables are the foundation of v0. They provide headless logic that you can use directly or through wrapper components. ## Composables vs Components Both approaches use the same underlying logic: ```ts // Direct composable usage const selection = createSelection({ multiple: true }) selection.register({ id: 'a', value: 'Apple' }) selection.select('a') ``` ```vue playground Apple ``` ### When to Use Each | Use Composables When | Use Components When | | - | - | | Need full control over rendering | Want declarative templates | | Building custom abstractions | Standard UI patterns | | Non-DOM contexts (stores, workers) | Accessibility attrs needed | | Maximum flexibility | Faster development | > [!TIP] > Components and composables are interchangeable. Every component uses a composable internally—you can always drop to the composable for more control. > [!SUGGESTION] How do I choose between composables and components for my use case? ## Categories ### Foundation Factories that create other composables: | Composable | Purpose | | - | - | | [createContext](/composables/foundation/create-context) | Type-safe provide/inject | | [createTrinity](/composables/foundation/create-trinity) | `[use, provide, context]` tuple | | [createPlugin](/composables/foundation/create-plugin) | Vue plugin factory | ### Registration Collection management primitives: | Composable | Purpose | | - | - | | [useRegistry](/composables/registration/use-registry) | Base collection with lookup | | [useTokens](/composables/registration/use-tokens) | Design token aliases | | [useQueue](/composables/registration/use-queue) | Time-based queue | | [useTimeline](/composables/registration/use-timeline) | Undo/redo history | ### Selection State management for selection patterns: | Composable | Purpose | | - | - | | [useSelection](/composables/selection/use-selection) | Multi-select base | | [useSingle](/composables/selection/use-single) | Radio, tabs, accordion | | [useGroup](/composables/selection/use-group) | Checkboxes, tri-state | | [useStep](/composables/selection/use-step) | Wizard, stepper, carousel | ### Forms Form state and validation: | Composable | Purpose | | - | - | | [useForm](/composables/forms/use-form) | Validation, dirty tracking | | [useProxyModel](/composables/forms/use-proxy-model) | v-model bridge | ### Plugins App-level features installed via `app.use()`: | Composable | Purpose | | - | - | | [useTheme](/composables/plugins/use-theme) | Dark/light mode | | [useLocale](/composables/plugins/use-locale) | i18n, RTL | | [useBreakpoints](/composables/plugins/use-breakpoints) | Responsive queries | | [useStorage](/composables/plugins/use-storage) | Persistent state | ### Utilities Standalone helpers: | Composable | Purpose | | - | - | | [useFilter](/composables/utilities/use-filter) | Array filtering | | [usePagination](/composables/utilities/use-pagination) | Page navigation | | [useVirtual](/composables/utilities/use-virtual) | Virtual scrolling | ## Usage Patterns ### Direct Factory Call For standalone instances: ```ts import { createSelection } from '@vuetify/v0' const tabs = createSelection({ multiple: false }) tabs.register({ id: 'home', value: 'Home' }) tabs.register({ id: 'about', value: 'About' }) tabs.select('home') ``` ### Context Injection For component tree sharing: ```ts // Parent import { createSelectionContext } from '@vuetify/v0' const [useTabSelection, provideTabSelection] = createSelectionContext({ namespace: 'tabs', multiple: false, }) provideTabSelection() // Child const selection = useTabSelection() selection.select('home') ``` ### Plugin Installation For app-wide singletons: ```ts main.ts import { createApp } from 'vue' import { createThemePlugin } from '@vuetify/v0' const app = createApp(App) app.use( createThemePlugin({ default: 'light', themes: { light: {...}, dark: {...} }, }), ) ``` ## Composing Composables Build complex behavior by combining primitives: ```ts composables/useDataTable.ts import { createSelection, createFilter, createPagination } from '@vuetify/v0' // Filterable, paginated selection const items = ref([...]) const query = ref('') const filter = createFilter() const { items: filtered } = filter.apply(query, items) const pagination = createPagination({ size: () => filtered.value.length, itemsPerPage: 10, }) const selection = createSelection({ multiple: true }) // Visible items with selection state const visibleItems = computed(() => { const start = pagination.pageStart.value const end = pagination.pageStop.value return filtered.value.slice(start, end) }) ``` ## TypeScript All composables are fully typed. The value type is inferred from registration: ```ts interface MyItem { id: string label: string } const selection = createSelection() selection.register({ id: '1', value: { id: '1', label: 'First' } as MyItem }) // Type-safe access via ticket const ticket = selection.get('1') ticket?.value // MyItem ``` > [!SUGGESTION] Which composables should I use for a data table with filtering and pagination? --- # Core URL: https://0.vuetifyjs.com/guide/core # Core v0's core architecture provides type-safe dependency injection and composable patterns. This page explains **how v0 works**. For creating plugins, see [Plugins Guide](/guide/plugins). ## Architecture Overview ```mermaid flowchart TD A[createContext] --> B[createTrinity] B --> C[createPlugin] A -.- A1["Type-safe provide/inject wrapper"] B -.- B1["[use, provide, context] tuple"] C -.- C1["Vue plugin factory"] ``` ## The Trinity Pattern The signature pattern of v0. Every composable returns a readonly 3-tuple: ```ts const [useTheme, provideTheme, theme] = createThemeContext() // 1. useTheme - Inject from ancestor const theme = useTheme() // 2. provideTheme - Provide to descendants provideTheme() // Use defaults provideTheme(customTheme) // Provide custom // 3. theme - Direct access without DI theme.cycle() // Cycle through themes ``` ### Why Three Elements? | Element | Purpose | | - | - | | `useContext` | Consume in child components | | `provideContext` | Provide from parent (defaults to built-in context) | | `defaultContext` | Standalone access, testing, outside Vue | > [!TIP] > The third element (`defaultContext`) is useful for unit testing without mounting Vue components. ## createContext Type-safe wrapper around Vue's provide/inject that **throws on missing context** (no silent undefined): ### Static Key Mode ```ts const [useTheme, provideTheme] = createContext('v0:theme') // Provider provideTheme({ isDark: ref(false), toggle: () => {} }) // Consumer - throws if not provided const theme = useTheme() ``` ### Dynamic Key Mode For nested contexts (panels within panels): ```ts const [usePanel, providePanel] = createContext() // Provider with runtime key providePanel('panel-1', context) // Consumer with same key const panel = usePanel('panel-1') ``` ### Suffix Pattern For parent-child context hierarchies: ```ts const [useItem, provideItem] = createContext({ suffix: 'item' }) provideItem('v0:panel', context) // Provides to 'v0:panel:item' useItem('v0:panel') // Injects from 'v0:panel:item' ``` ## Registry System `useRegistry` is the foundational data structure. All selection, forms, and token composables extend it. ```ts const registry = useRegistry() // Register items const ticket = registry.register({ id: 'item-1', value: 'First' }) // Lookup registry.get('item-1') // Get by ID registry.browse('First') // Get IDs by value registry.lookup(0) // Get ID by index // Cleanup registry.unregister('item-1') ``` ### Ticket Structure Every registered item is a "ticket": ```ts interface RegistryTicket { id: ID, // Unique identifier index: number, // Position in registry value: unknown, // Associated data valueIsIndex: boolean, // True if value wasn't explicitly set } ``` ### Extension Chain ```mermaid flowchart LR R[useRegistry] --> S[useSelection] R --> T[useTokens] R --> F[useForm] S --> Si[useSingle] S --> G[useGroup] Si --> St[useStep] ``` | Composable | Extends | Adds | | - | - | - | | `useRegistry` | — | Base collection management | | `useSelection` | Registry | `selectedIds` Set | | `useSingle` | Selection | Single selection constraint | | `useGroup` | Selection | Tri-state, batch ops | | `useStep` | Single | Navigation (next/prev/first/last) | | `useTokens` | Registry | Alias resolution | | `useForm` | Registry | Validation | ## Selection System ### useSelection Base multi-selection with Set-based tracking: ```ts const selection = createSelection({ multiple: true }) selection.register({ id: 'a', value: 'Apple' }) selection.register({ id: 'b', value: 'Banana' }) selection.select('a') selection.selectedIds // Set { 'a' } ``` ### useSingle Auto-clears previous selection: ```ts const tabs = createSingle({ mandatory: true }) tabs.onboard([ { id: 'tab-1', value: 'Home' }, { id: 'tab-2', value: 'About' }, ]) tabs.select('tab-1') tabs.select('tab-2') // tab-1 auto-unselected tabs.selectedId.value // 'tab-2' ``` ### useGroup Multi-select with tri-state: ```ts const checkboxes = createGroup() checkboxes.onboard([ { id: 'a', value: 'Option A' }, { id: 'b', value: 'Option B' }, { id: 'c', value: 'Option C' }, ]) checkboxes.select(['a', 'b']) checkboxes.selectAll() checkboxes.isMixed.value // true when some (not all) selected ``` ### useStep Sequential navigation: ```ts const wizard = createStep({ circular: false }) wizard.onboard([ { id: 'step-1', value: 'Step 1' }, { id: 'step-2', value: 'Step 2' }, { id: 'step-3', value: 'Step 3' }, ]) wizard.first() // Select first step wizard.next() // Move forward wizard.prev() // Move backward wizard.last() // Jump to end ``` ## Best Practices ### Naming Conventions | Prefix | Purpose | Example | | - | - | - | | `use*` | Inject from context | `useTheme()` | | `create*` | Factory returning instance | `createSelection()` | | `create*Context` | Factory returning trinity | `createThemeContext()` | | `create*Plugin` | Factory returning Vue plugin | `createThemePlugin()` | ### When to Use Each | Need | Use | | - | - | | Share state in component tree | `provideContext` / `useContext` | | App-wide singleton | `createPlugin` with `app.use()` | | Standalone logic | Direct factory call | | Testing | Trinity's third element | > [!SUGGESTION] How do I handle scoped contexts for nested components without prop drilling? --- # Features URL: https://0.vuetifyjs.com/guide/features # Features Review and select from a variety of features to enhance your UI library --- # Nuxt URL: https://0.vuetifyjs.com/guide/nuxt # Nuxt 3 v0 integrates with Nuxt 3 through standard Vue plugin registration. This guide covers SSR considerations, auto-imports, and theme persistence. ## Basic Setup See [Getting Started](/introduction/getting-started#nuxt-3) for the minimal plugin setup. ## Auto-Imports Configure Nuxt to auto-import v0 composables: ```ts nuxt.config.ts export default defineNuxtConfig({ build: { transpile: ['@vuetify/v0'], }, imports: { imports: [ { from: '@vuetify/v0', name: 'useTheme' }, { from: '@vuetify/v0', name: 'useSelection' }, { from: '@vuetify/v0', name: 'useGroup' }, { from: '@vuetify/v0', name: 'useSingle' }, { from: '@vuetify/v0', name: 'useStep' }, { from: '@vuetify/v0', name: 'usePagination' }, { from: '@vuetify/v0', name: 'useForm' }, { from: '@vuetify/v0', name: 'useHydration' }, { from: '@vuetify/v0', name: 'useBreakpoints' }, { from: '@vuetify/v0', name: 'IN_BROWSER' }, ], }, }) ``` ## SSR Considerations ### The `IN_BROWSER` Constant Guard browser-only code with `IN_BROWSER`: ```ts import { IN_BROWSER } from '@vuetify/v0' if (IN_BROWSER) { localStorage.setItem('key', 'value') window.addEventListener('resize', handler) } ``` ### Hydration State Use `useHydration` to defer browser-only rendering: ```vue ``` The hydration plugin: - Sets `isHydrated` to `false` during SSR - Flips to `true` after the root component mounts on client - Falls back gracefully if not installed ### Theme SSR Integration The theme plugin automatically integrates with Nuxt's `@unhead/vue` to inject styles during SSR. No additional configuration required. ## Theme Persistence > [!TIP] > Use cookies instead of localStorage for theme persistence. Cookies are available on the server, preventing flash of wrong theme. For theme preference to persist across SSR requests, use cookies instead of localStorage: ```ts plugins/v0.ts import { createHydrationPlugin, createThemePlugin, IN_BROWSER } from '@vuetify/v0' export default defineNuxtPlugin((nuxtApp) => { const themeCookie = useCookie<'light' | 'dark'>('theme', { default: () => 'light', }) function resolveTheme(): 'light' | 'dark' { if (themeCookie.value) return themeCookie.value if (!IN_BROWSER) return 'light' return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' } nuxtApp.vueApp.use(createHydrationPlugin()) nuxtApp.vueApp.use( createThemePlugin({ default: resolveTheme(), themes: { light: { dark: false, colors: { primary: '#3b82f6', surface: '#ffffff', 'on-primary': '#ffffff', 'on-surface': '#212121', }, }, dark: { dark: true, colors: { primary: '#60a5fa', surface: '#1e1e1e', 'on-primary': '#1a1a1a', 'on-surface': '#e0e0e0', }, }, }, }), ) }) ``` To sync theme changes back to the cookie: ```vue ``` ## SSR Compatibility Reference | Feature | SSR Support | Notes | | - | - | - | | Components | Full | All compound components work in SSR | | `useTheme` | Full | Auto-injects styles via Nuxt head manager | | `useHydration` | Full | Designed for SSR/client state sync | | `useBreakpoints` | Partial | Returns defaults on server, measures on client | | `useStorage` | Partial | Uses memory adapter on server | | `usePagination` | Full | Width-based calculation defers to client | | Observer composables | Partial | No-op on server, activate on client | ## Common Patterns ### Client-Only Components For components that can't render on the server: ```vue ``` ### Avoiding Hydration Mismatch Common causes: - Timestamps or random values during render - Conditional rendering based on browser state - Dynamic IDs without SSR-safe generation ```vue ``` > [!SUGGESTION] How do I debug hydration mismatches in my Nuxt components? > [!SUGGESTION] When should I use useHydration vs ClientOnly components? --- # Plugins URL: https://0.vuetifyjs.com/guide/plugins # Plugins v0 plugins are Vue plugins built with `createPlugin`. They provide app-wide singletons for features like theming, locale, and logging. For understanding the underlying architecture, see [Core](/guide/core). ## Using Built-in Plugins ### Basic Installation ```ts main.ts import { createApp } from 'vue' import { createThemePlugin, createLocalePlugin } from '@vuetify/v0' const app = createApp(App) // Install plugins app.use(createThemePlugin()) app.use(createLocalePlugin()) app.mount('#app') ``` ### With Options ```ts app.use( createThemePlugin({ default: 'dark', themes: { light: { dark: false, colors: { primary: '#1976D2' } }, dark: { dark: true, colors: { primary: '#2196F3' } }, }, }), ) app.use( createLocalePlugin({ default: 'en', messages: { en: { hello: 'Hello' } }, }), ) ``` ## Available Plugins | Plugin | Purpose | Composable | | - | - | - | | `createThemePlugin` | CSS variable theming, dark mode | [useTheme](/composables/plugins/use-theme) | | `createLocalePlugin` | i18n, RTL support | [useLocale](/composables/plugins/use-locale) | | `createLoggerPlugin` | Structured logging | [useLogger](/composables/plugins/use-logger) | | `createStoragePlugin` | Reactive localStorage/sessionStorage | [useStorage](/composables/plugins/use-storage) | | `createPermissionsPlugin` | Role-based access control | [usePermissions](/composables/plugins/use-permissions) | | `createBreakpointsPlugin` | Responsive breakpoint detection | [useBreakpoints](/composables/plugins/use-breakpoints) | | `createHydrationPlugin` | SSR hydration management | [useHydration](/composables/plugins/use-hydration) | > [!TIP] > All plugins are optional. Only install what you need—v0 works without any plugins installed. > [!SUGGESTION] Which built-in plugins do I actually need? Can I use v0 without them? ## Creating Custom Plugins ### Basic Plugin ```ts plugins/analytics.ts import { createContext, createPlugin } from '@vuetify/v0' interface AnalyticsContext { track: (event: string, data?: Record) => void identify: (userId: string) => void } // 1. Create the context for DI const [useAnalytics, provideAnalytics] = createContext('my:analytics') // 2. Create the plugin factory export function createAnalyticsPlugin() { const context: AnalyticsContext = { track: (event, data) => console.log('Track:', event, data), identify: (userId) => console.log('Identify:', userId), } return createPlugin({ namespace: 'my:analytics', provide: (app) => provideAnalytics(context, app), }) } export { useAnalytics } ``` ### Plugin with Options ```ts plugins/analytics.ts interface AnalyticsOptions { apiKey: string debug?: boolean } export function createAnalyticsPlugin(options: AnalyticsOptions) { const { apiKey, debug = false } = options const context: AnalyticsContext = { track: (event, data) => { if (debug) console.log('Track:', event, data) // Send to analytics service }, identify: (userId) => { // Identify user }, } return createPlugin({ namespace: 'my:analytics', provide: (app) => provideAnalytics(context, app), }) } // Usage app.use(createAnalyticsPlugin({ apiKey: 'xxx', debug: true })) ``` ### Plugin with Adapter Pattern For extensible plugins that support multiple backends: ```ts plugins/logger.ts interface LoggerAdapter { log: (level: string, message: string) => void } interface LoggerContext { info: (msg: string) => void warn: (msg: string) => void error: (msg: string) => void } interface LoggerOptions { adapter?: LoggerAdapter } const consoleAdapter: LoggerAdapter = { log: (level, message) => console[level](message), } const [useLogger, provideLogger] = createContext('my:logger') export function createLoggerPlugin(options: LoggerOptions = {}) { const adapter = options.adapter ?? consoleAdapter const context: LoggerContext = { info: (msg) => adapter.log('info', msg), warn: (msg) => adapter.log('warn', msg), error: (msg) => adapter.log('error', msg), } return createPlugin({ namespace: 'my:logger', provide: (app) => provideLogger(context, app), }) } // Custom adapter example const sentryAdapter: LoggerAdapter = { log: (level, message) => { if (level === 'error') Sentry.captureMessage(message) }, } app.use(createLoggerPlugin({ adapter: sentryAdapter })) ``` ## Consuming Plugins ```vue ``` ## Plugin vs Context | Need | Use | | - | - | | App-wide singleton (one instance) | Plugin with `app.use()` | | Multiple instances per subtree | Context with `provideContext` | | Configurable at install time | Plugin | | Configurable per component tree | Context | ## Best Practices ### 1. Provide Default Adapters ```ts // Always provide a sensible default const defaultAdapter = { /* ... */ } const adapter = options.adapter ?? defaultAdapter ``` ### 2. Type the Context Interface ```ts // Define what consumers get interface ThemeContext { selectedId: ComputedRef select: (id: string) => void cycle: () => void } ``` ### 3. Handle Missing Installation ```ts const theme = useTheme() // useTheme throws if createThemePlugin isn't installed // This is intentional - fail fast ``` --- # Structure URL: https://0.vuetifyjs.com/guide/structure # Structure Quick reference for v0's codebase organization. Use this when navigating the source or deciding where to add new features. ## Package Layout ```txt @vuetify/v0 (packages/0/src/) ├── components/ # Vue component wrappers (one folder per component) ├── composables/ # Core logic (flat structure, one folder per composable) │ ├── createContext/ │ ├── useRegistry/ │ ├── useSelection/ │ └── ... # All composables at same level ├── constants/ # Shared constants (IN_BROWSER, etc.) ├── types/ # Shared TypeScript types ├── utilities/ # Helper functions └── index.ts # Public exports ``` > [!TIP] > Composables use a flat directory structure. The categories below are logical groupings for documentation purposes only. ## Composable Categories | Category | Purpose | Key Exports | | - | - | - | | **foundation** | Core factories | `createContext`, `createTrinity`, `createPlugin` | | **registration** | Collection management | `useRegistry`, `useTokens`, `useQueue`, `useTimeline`, `useProxyRegistry` | | **selection** | Selection state | `useSelection`, `useSingle`, `useGroup`, `useStep` | | **forms** | Form handling | `useForm`, `useProxyModel` | | **system** | Browser APIs | `useEventListener`, `useKeydown`, `useResizeObserver`, `useClickOutside`, `useIntersectionObserver`, `useMutationObserver`, `useToggleScope` | | **plugins** | App features | `useTheme`, `useLocale`, `useLogger`, `useStorage`, `useBreakpoints`, `useFeatures`, `useHydration`, `usePermissions` | | **utilities** | UI helpers | `useFilter`, `usePagination`, `useVirtual`, `useOverflow` | | **transformers** | Value transforms | `toArray`, `toReactive` | ## Component Categories | Category | Purpose | Components | | - | - | - | | **primitives** | Base building blocks | `Atom` | | **providers** | Pure state, no DOM | `Selection`, `Single`, `Group`, `Step` | | **semantic** | Meaningful HTML | `Avatar`, `Pagination` | | **disclosure** | Show/hide patterns | `ExpansionPanel`, `Popover` | ## Import Patterns ### Named Imports ```ts import { useSelection, createThemePlugin, Atom } from '@vuetify/v0' ``` ### Compound Components ```ts import { Selection, ExpansionPanel } from '@vuetify/v0' // Usage: Selection.Root, Selection.Item // Usage: ExpansionPanel.Root, ExpansionPanel.Item, ExpansionPanel.Header, // ExpansionPanel.Activator, ExpansionPanel.Content ``` ## File Conventions ### Composables ```txt composables/ └── useSelection/ ├── index.ts # Main composable └── index.test.ts # Colocated tests ``` ### Components ```txt components/ └── Selection/ ├── SelectionRoot.vue # Container component ├── SelectionItem.vue # Child component ├── index.ts # Compound export + re-exports └── index.test.ts # Colocated tests ``` ## Naming Conventions | Prefix | Purpose | Example | | - | - | - | | `use*` | Composable (inject from context) | `useTheme()` | | `create*` | Factory returning instance | `createSelection()` | | `create*Context` | Factory returning trinity | `createThemeContext()` | | `create*Plugin` | Factory returning Vue plugin | `createThemePlugin()` | | `to*` | Value transformer | `toArray()`, `toReactive()` | ## Extension Hierarchy ```mermaid flowchart TD Registry[useRegistry] Selection[useSelection] Single[useSingle] Step[useStep] Group[useGroup] Tokens[useTokens] Form[useForm] Registry --> Selection Registry --> Tokens Registry --> Form Selection --> Single Selection --> Group Single --> Step ``` > [!SUGGESTION] When should I use useSelection vs useSingle vs useGroup for my use case? --- # Theming URL: https://0.vuetifyjs.com/guide/theming # Theming v0 theming uses CSS custom properties for runtime theme switching. The theme plugin manages variable injection; you style with standard CSS. > [!TIP] > v0 is unopinionated—you define all theme colors. The examples below show common patterns, but the color names and values are entirely yours. > [!SUGGESTION] How do I use v0 theming with Tailwind CSS instead of UnoCSS? ## Quick Start ### 1. Install Plugin ```ts main.ts import { createApp } from 'vue' import { createThemePlugin } from '@vuetify/v0' const app = createApp(App) app.use( createThemePlugin({ default: 'light', themes: { light: { dark: false, colors: { primary: '#1976D2', secondary: '#424242', background: '#FFFFFF', surface: '#FFFFFF', error: '#B00020', }, }, dark: { dark: true, colors: { primary: '#2196F3', secondary: '#616161', background: '#121212', surface: '#1E1E1E', error: '#CF6679', }, }, }, }), ) ``` ### 2. Use in Components ```vue playground Current: {{ theme.selectedId }} ``` ## Theme API ```ts const theme = useTheme() // Read current theme theme.selectedId.value // 'light' | 'dark' theme.selectedItem.value // Current theme ticket theme.isDark.value // boolean // Switch themes theme.cycle() // Cycle through themes theme.select('dark') // Select specific theme // Access theme values theme.colors.value // Map of all theme colors theme.colors.value[theme.selectedId.value] // Current theme's colors ``` ## CSS Variables v0 injects variables with the `--v0-` prefix: | Variable | Purpose | | - | - | | `--v0-primary` | Primary brand color | | `--v0-secondary` | Secondary color | | `--v0-background` | Page background | | `--v0-surface` | Card/component background | | `--v0-error` | Error state color | | `--v0-on-primary` | Text on primary background | | `--v0-on-surface` | Text on surface background | ### Using Variables ```css .btn-primary { background: var(--v0-primary); color: var(--v0-on-primary); } .card { background: var(--v0-surface); border: 1px solid var(--v0-divider); } ``` ### UnoCSS Integration Map v0 variables to UnoCSS theme colors for utility class support: ```ts uno.config.ts import { defineConfig, presetUno } from 'unocss' export default defineConfig({ presets: [presetUno()], theme: { colors: { primary: 'var(--v0-primary)', secondary: 'var(--v0-secondary)', background: 'var(--v0-background)', surface: 'var(--v0-surface)', error: 'var(--v0-error)', 'on-primary': 'var(--v0-on-primary)', 'on-surface': 'var(--v0-on-surface)', }, }, }) ``` Now use standard utilities: ```html Click me Card ``` Theme changes update automatically—the utilities reference CSS variables, not static values. ## Dark Mode ### Manual Toggle ```vue playground {{ theme.isDark ? 'Light' : 'Dark' }} ``` > [!SUGGESTION] How can I sync theme selection to localStorage and restore it on page load? ## Design Tokens with createTokens For fine-grained token management beyond colors: ```ts import { createTokens } from '@vuetify/v0' const tokens = createTokens() // Register tokens tokens.register({ id: 'spacing-sm', value: '0.5rem' }) tokens.register({ id: 'spacing-md', value: '1rem' }) tokens.register({ id: 'radius-sm', value: '4px' }) // Aliases use {path} syntax in the value tokens.register({ id: 'gap', value: '{spacing-md}' }) // Lookup resolves aliases tokens.resolve('gap') // '1rem' ``` ### Token Categories ```ts // Typography tokens.register({ id: 'font-body', value: 'Inter, sans-serif' }) tokens.register({ id: 'font-size-sm', value: '0.875rem' }) // Spacing tokens.register({ id: 'space-1', value: '0.25rem' }) tokens.register({ id: 'space-2', value: '0.5rem' }) // Shadows tokens.register({ id: 'shadow-sm', value: '0 1px 2px rgba(0,0,0,0.1)' }) ``` ## Scoped Themes Override theme for a subtree using the trinity pattern: ```ts import { createThemeContext, createTheme } from '@vuetify/v0' // Create a custom theme context with options const [useTheme, provideTheme] = createThemeContext({ default: 'custom', themes: { custom: { dark: false, colors: { primary: '#E91E63', // Pink instead of blue secondary: '#9C27B0', }, }, }, }) // Provide to descendants provideTheme() ``` ## Best Practices ### 1. Semantic Color Names ```ts // Good - semantic themes: { light: { colors: { primary: '#1976D2', error: '#B00020' } }, } // Avoid - literal themes: { light: { colors: { blue: '#1976D2', red: '#B00020' } }, } ``` ### 2. On-* Contrast Colors Always define contrast colors for accessibility: ```ts themes: { light: { colors: { primary: '#1976D2', 'on-primary': '#FFFFFF', // White text on primary surface: '#FFFFFF', 'on-surface': '#212121', // Dark text on surface }, }, } ``` ### 3. CSS Fallbacks ```css .card { /* Fallback for missing variable */ background: var(--v0-surface, #ffffff); } ``` --- # Utilities URL: https://0.vuetifyjs.com/guide/utilities # Utilities Standalone helpers for common UI patterns. These composables don't depend on context or plugins—use them anywhere. ## Overview | Utility | Purpose | | - | - | | [createFilter](/composables/utilities/use-filter) | Filter arrays with search queries | | [createPagination](/composables/utilities/use-pagination) | Page navigation state | | [useVirtual](/composables/utilities/use-virtual) | Virtual scrolling for large lists | | [createOverflow](/composables/utilities/use-overflow) | Compute visible item capacity | > [!TIP] > These utilities are standalone—they don't require plugins or context. Use them anywhere, including outside Vue components. ## createFilter Filter arrays based on search queries: ```ts import { createFilter } from '@vuetify/v0' const items = ref(['Apple', 'Banana', 'Cherry']) const query = ref('') const filter = createFilter() const { items: filtered } = filter.apply(query, items) query.value = 'an' filtered.value // ['Banana'] ``` ### With Object Keys ```ts const users = ref([ { name: 'Alice', email: 'alice@example.com' }, { name: 'Bob', email: 'bob@example.com' }, ]) const filter = createFilter({ keys: ['name', 'email'], }) const query = ref('alice') const { items: filtered } = filter.apply(query, users) filtered.value // [{ name: 'Alice', ... }] ``` ### Filter Modes ```ts const filter = createFilter({ mode: 'intersection', // 'some' | 'every' | 'union' | 'intersection' keys: ['name', 'tags'], }) ``` ## createPagination Pagination state management: ```ts import { createPagination } from '@vuetify/v0' const pagination = createPagination({ size: 100, itemsPerPage: 10, }) pagination.page.value // 1 pagination.pages // 10 pagination.isFirst.value // true pagination.isLast.value // false pagination.next() // Go to page 2 pagination.prev() // Go to page 1 pagination.select(5) // Go to page 5 pagination.first() // Go to page 1 pagination.last() // Go to page 10 ``` ### With Reactive Size ```ts const items = ref([...]) const pagination = createPagination({ size: () => items.value.length, itemsPerPage: 20, }) ``` ### Page Items ```ts pagination.items.value // [{ type: 'page', value: 1 }, { type: 'page', value: 2 }, ...] ``` ## useVirtual Virtual scrolling for large datasets: ```ts import { useVirtual } from '@vuetify/v0' const items = ref(Array.from({ length: 10000 }, (_, i) => `Item ${i}`)) // useVirtual takes items as first arg, options as second const virtual = useVirtual(items, { itemHeight: 40, }) ``` ```vue playground VirtualList.vue {{ item.raw }} ``` ### Variable Height ```ts const virtual = useVirtual(items, { height: 400, // Container height (or use element ref) }) ``` ## createOverflow Compute how many items fit in a container: ```ts import { createOverflow } from '@vuetify/v0' const container = ref() const overflow = createOverflow({ container, itemWidth: 100, gap: 8, }) overflow.capacity.value // Number of items that fit overflow.isOverflowing.value // Boolean: items exceed capacity ``` ### Use Case: Responsive Chips ```vue playground ResponsiveChips.vue {{ tag }} +{{ tags.length - overflow.capacity.value }} ``` ## Transformers Value transformation utilities: ### toArray Normalize any value to an array: ```ts import { toArray } from '@vuetify/v0' toArray('single') // ['single'] toArray(['array']) // ['array'] toArray(null) // [] toArray(undefined) // [] ``` ### toReactive Convert ref objects to reactive proxies: ```ts import { toReactive } from '@vuetify/v0' const configRef = ref({ debug: false }) // Unwraps the ref and returns a reactive object const config = toReactive(configRef) config.debug // Reactive access ``` ## Best Practices ### Combine Utilities ```ts // Filter + Paginate const query = ref('') const filter = createFilter() const { items: filtered } = filter.apply(query, items) const pagination = createPagination({ size: () => filtered.value.length, itemsPerPage: 10, }) const displayedItems = computed(() => { const start = pagination.pageStart.value const end = pagination.pageStop.value return filtered.value.slice(start, end) }) ``` ### Virtual + Filter ```ts const query = ref('') const filter = createFilter() const { items: filtered } = filter.apply(query, items) const virtual = useVirtual(filtered, { itemHeight: 40, }) ``` > [!SUGGESTION] How do I combine filter, pagination, AND virtual scrolling together? --- # Vuetify Mcp URL: https://0.vuetifyjs.com/guide/vuetify-mcp # Vuetify MCP Vuetify MCP is a [Model Context Protocol](https://modelcontextprotocol.io/) server that gives AI assistants structured access to Vuetify and v0 APIs. Unlike [llms.txt](/guide/ai-tools), MCP provides real-time, queryable documentation. ## Quick Start ### Hosted Server (Recommended) The fastest way to get started. No installation required: ```bash claude mcp add --transport http vuetify-mcp https://mcp.vuetifyjs.com/mcp ``` ### Local Installation Run the server locally for offline access: ```bash npx -y @vuetify/mcp ``` ### Interactive Setup Auto-detects your IDE and configures MCP: ```bash # For hosted server npx -y @vuetify/mcp config --remote # For local server npx -y @vuetify/mcp config ``` ## IDE Configuration Manual configuration for each IDE: | IDE | Config File | | - | - | | Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) | | VS Code | `~/.config/Code/User/settings.json` | | Cursor | `~/.config/Cursor/User/mcp.json` | | Windsurf | `~/.config/Windsurf/User/mcp.json` | | Trae | `~/.config/Trae/User/mcp.json` | ### Hosted Configuration ```json { "mcpServers": { "vuetify-mcp": { "url": "https://mcp.vuetifyjs.com/mcp" } } } ``` ### Local Configuration ```json { "mcpServers": { "vuetify-mcp": { "command": "npx", "args": ["-y", "@vuetify/mcp"] } } } ``` ## Available Tools ### Vuetify 3 API | Tool | Purpose | | - | - | | `get_component_api_by_version` | Props, events, slots for any component | | `get_directive_api_by_version` | Directive info (v-ripple, v-scroll, etc.) | | `get_vuetify_api_by_version` | Download full API types by version | ### Documentation | Tool | Purpose | | - | - | | `get_installation_guide` | Setup for Vite, Nuxt, Laravel, CDN | | `get_feature_guide` | Theming, i18n, accessibility guides | | `get_frequently_asked_questions` | Common questions and answers | | `get_release_notes_by_version` | Changelog for any version | ### v0 (Headless) | Tool | Purpose | | - | - | | `get_vuetify0_composable_list` | List all composables by category | | `get_vuetify0_composable_guide` | Detailed composable documentation | | `get_vuetify0_component_list` | List all headless components | | `get_vuetify0_component_guide` | Component documentation and examples | > [!SUGGESTION] Which Vuetify MCP tools should I use for building components? ## Authentication Some features require a Vuetify API key: ```json { "mcpServers": { "vuetify-mcp": { "command": "npx", "args": ["-y", "@vuetify/mcp"], "env": { "VUETIFY_API_KEY": "your-api-key" } } } } ``` ## Self-Hosting Run an HTTP server for team access: ```bash npx -y @vuetify/mcp --transport=http --port=3000 --host=0.0.0.0 --stateless ``` Then configure clients to use your server URL instead of `mcp.vuetifyjs.com`. --- # components URL: https://0.vuetifyjs.com/components # Components A collection of foundational components designed to be headless, accessible, and highly customizable. ## Primitives Foundation components for building higher-level abstractions. | Name | Description | | - | - | | [Atom](/components/primitives/atom) | Polymorphic element with dynamic `as` prop and renderless mode | ## Providers Pure context providers for state management. Always renderless—they provide logic without rendering DOM elements. | Name | Description | | - | - | | [Selection](/components/providers/selection) | Multi-selection state with v-model binding | | [Single](/components/providers/single) | Single-selection with automatic deselection | | [Group](/components/providers/group) | Multi-selection with tri-state support | | [Step](/components/providers/step) | Sequential navigation (first, last, next, prev) | ## Semantic Components with meaningful HTML defaults. Render semantic elements by default but support the `as` prop for customization. | Name | Description | | - | - | | [Avatar](/components/semantic/avatar) | Image/fallback avatar with priority loading | | [Pagination](/components/semantic/pagination) | Page navigation with semantic `` wrapper | ## Disclosure Components for showing/hiding content. | Name | Description | | - | - | | [ExpansionPanel](/components/disclosure/expansion-panel) | Accordion-style collapsible panels | | [Popover](/components/disclosure/popover) | CSS anchor-positioned popup content | --- # Expansion Panel URL: https://0.vuetifyjs.com/components/disclosure/expansion-panel # ExpansionPanel A component for creating accordion-style expansion panels with proper ARIA support. ## Usage The ExpansionPanel component provides a wrapper and item pattern for managing expansion state in accordion-style interfaces. It uses the `useSelection` composable internally and provides full v-model support with automatic state synchronization. ## Anatomy ```vue Anatomy playground --- # Popover URL: https://0.vuetifyjs.com/components/disclosure/popover # Popover A headless component for creating popovers and tooltips using modern CSS anchor positioning. ## Usage The Popover component leverages the CSS Anchor Positioning API to create popovers, tooltips, and dropdown menus without JavaScript-based positioning. It provides v-model support for open/closed state management. ## Anatomy ```vue Anatomy playground ``` ## Positioning The Popover component uses the CSS Anchor Positioning API for positioning. The `positionArea` prop accepts standard CSS values: | Value | Description | |---|---| | `top` | Position above the anchor | | `bottom` | Position below the anchor | | `left` | Position to the left of the anchor | | `right` | Position to the right of the anchor | | `top left` | Position above and to the left | | `top right` | Position above and to the right | | `bottom left` | Position below and to the left | | `bottom right` | Position below and to the right | The `positionTry` prop provides fallback positioning when the primary position doesn't fit. --- # Atom URL: https://0.vuetifyjs.com/components/primitives/atom # Atom The foundational building block for dynamic element rendering with renderless capabilities. ## Usage The Atom component provides dynamic element rendering and is used as the foundation for all other components in Vuetify0. It supports polymorphic rendering through the `as` prop and can render as any HTML element or Vue component. ## Anatomy ```vue Anatomy playground ``` --- # Group URL: https://0.vuetifyjs.com/components/providers/group # Group A headless component for managing multi-selection with batch operations and tri-state support. ## Usage The Group component is a specialization of Selection that enforces multi-selection behavior and supports batch operations on arrays of IDs. It always uses array-based v-model binding. ## Anatomy ```vue Anatomy playground --- # Selection URL: https://0.vuetifyjs.com/components/providers/selection # Selection A headless component for managing selection state in collections with support for single and multi-selection patterns. ## Usage The Selection component provides a wrapper and item pattern for managing selection state in collections. It uses the `useSelection` composable internally and provides full v-model support with automatic state synchronization. ## Anatomy ```vue Anatomy playground ### Mandatory Selection ### Disabled Items --- # Single URL: https://0.vuetifyjs.com/components/providers/single # Single A headless component for managing single-selection with automatic deselection of previous items. ## Usage The Single component is a specialization of Selection that enforces single-selection behavior. When an item is selected, any previously selected item is automatically deselected. ## Anatomy ```vue Anatomy playground ``` --- # Step URL: https://0.vuetifyjs.com/components/providers/step # Step A headless component for navigation through multi-step processes like wizards and forms. ## Usage The Step component extends Single with navigation methods for moving through a sequence of items. It provides methods for first, last, next, previous, and step-by-count navigation with automatic disabled item skipping. ## Anatomy ```vue Anatomy playground ``` ## Navigation The Step component provides several navigation methods: | Method | Description | |---|---| | `first()` | Go to the first non-disabled item | | `last()` | Go to the last non-disabled item | | `next()` | Go to the next non-disabled item | | `prev()` | Go to the previous non-disabled item | | `step(count)` | Step forward (positive) or backward (negative) by count | All navigation methods automatically skip disabled items. --- # Avatar URL: https://0.vuetifyjs.com/components/semantic/avatar # Avatar A headless component for managing image loading with priority-based fallback system. ## Usage The Avatar component provides a robust image loading system with automatic fallback handling. It manages multiple image sources with priority ordering and only displays the highest-priority loaded image or fallback content. ## Anatomy ```vue Anatomy playground ``` ## Priority System The Avatar component uses a priority-based system to determine which content to display: 1. Images register with a `priority` value (default: `0`) 2. Fallbacks register with the lowest implicit priority 3. When an image loads successfully, it becomes selectable 4. The highest-priority loaded image is displayed 5. If all images fail, the fallback is shown ```vue PriorityExample JD ``` --- # Pagination URL: https://0.vuetifyjs.com/components/semantic/pagination # Pagination A headless component for creating page navigation with proper ARIA support. ## Usage The Pagination component provides a compound component pattern for building page navigation interfaces. It uses the [usePagination](/composables/utilities/use-pagination) and [useOverflow](/composables/utilities/use-overflow) composable internally. ## Anatomy ```vue Anatomy playground ``` > For responsive sizing to work accurately, **all pagination buttons must have the same width**. The component measures a sample button and uses that width to calculate how many buttons fit. If buttons have variable widths (e.g., single-digit "1" vs double-digit "50"), the calculation will be inaccurate and items may overflow or leave excess space. ## Recipes Examples of common Pagination structures: ### RouterLink Use the **as** prop to render pagination items as `RouterLink` components. ```vue RouterLink « ‹ {{ item.value }} › » ``` ### Unordered list Pagination example with nav, ul, and li elements. ```vue UnorderedList « ‹ {{ item.value }} › » ``` --- # composables URL: https://0.vuetifyjs.com/composables # Composables v0 composables are the building blocks for headless UI. Components wrap these composables—you can use either approach. ## Composable Hierarchy ``` createContext ─── Type-safe provide/inject │ └──► createTrinity ─── [use, provide, context] tuple │ └──► createPlugin ─── Vue plugin factory useRegistry ─── Base collection management │ ├──► useSelection ─── + selectedIds Set │ │ │ ├──► useSingle ─── Single selection │ │ │ │ │ └──► useStep ─── + navigation │ │ │ └──► useGroup ─── + tri-state, batch ops │ ├──► useTokens ─── + alias resolution │ └──► useForm ─── + validation ``` ## Choosing a Composable | Need | Use | Category | | - | - | - | | Radio buttons, tabs | `useSingle` | selection | | Checkboxes, multi-select | `useGroup` | selection | | Wizard, stepper | `useStep` | selection | | Form validation | `useForm` | forms | | Design tokens | `useTokens` | registration | | Dark/light mode | `useTheme` | plugins | | i18n, RTL | `useLocale` | plugins | | Filter/search | `useFilter` | utilities | | Pagination | `usePagination` | utilities | | Large lists | `useVirtual` | utilities | ## Foundation Core factories that provide the foundation for all other composables. | Name | Description | | - | - | | [createContext](/composables/foundation/create-context) | Create reusable context to share state across components | | [createPlugin](/composables/foundation/create-plugin) | Create Vue plugins with standardized patterns | | [createTrinity](/composables/foundation/create-trinity) | Create context provider/consumer pattern utilities | ## Registration Collection management and data structure primitives. | Name | Description | | - | - | | [useRegistry](/composables/registration/use-registry) | Foundation for registration-based systems | | [useProxyRegistry](/composables/registration/use-proxy-registry) | Proxy-based registry with automatic reactivity | | [useQueue](/composables/registration/use-queue) | Time-based queue management with automatic timeouts | | [useTimeline](/composables/registration/use-timeline) | Bounded undo/redo system with fixed-size history | | [useTokens](/composables/registration/use-tokens) | Design token management system | ## Selection State management for single and multi-selection patterns. | Name | Description | | - | - | | [useSelection](/composables/selection/use-selection) | General selection state management | | [useSingle](/composables/selection/use-single) | Single-selection with automatic deselection | | [useGroup](/composables/selection/use-group) | Multi-selection with tri-state support | | [useStep](/composables/selection/use-step) | Sequential navigation for wizards and steppers | ## Forms Form state management and model binding utilities. | Name | Description | | - | - | | [useForm](/composables/forms/use-form) | Form state management and validation | | [useProxyModel](/composables/forms/use-proxy-model) | Bridge selection context to v-model binding | ## System Browser API wrappers with automatic lifecycle cleanup. | Name | Description | | - | - | | [useEventListener](/composables/system/use-event-listener) | Handle DOM events with automatic cleanup | | [useIntersectionObserver](/composables/system/use-intersection-observer) | Intersection Observer API for visibility detection | | [useKeydown](/composables/system/use-keydown) | Handle keyboard events with automatic cleanup | | [useMutationObserver](/composables/system/use-mutation-observer) | Mutation Observer API for DOM change detection | | [useResizeObserver](/composables/system/use-resize-observer) | Resize Observer API for element size changes | | [useToggleScope](/composables/system/use-toggle-scope) | Conditional effect scope management | ## Plugins Application-level features installable via Vue plugins. | Name | Description | | - | - | | [useBreakpoints](/composables/plugins/use-breakpoints) | Responsive breakpoint detection | | [useFeatures](/composables/plugins/use-features) | Feature flags and A/B testing | | [useHydration](/composables/plugins/use-hydration) | SSR hydration management | | [useLocale](/composables/plugins/use-locale) | Internationalization system | | [useLogger](/composables/plugins/use-logger) | Logging system with multiple adapters | | [usePermissions](/composables/plugins/use-permissions) | Role-based access control | | [useStorage](/composables/plugins/use-storage) | Reactive browser storage interface | | [useTheme](/composables/plugins/use-theme) | Theme management with CSS custom properties | ## Utilities Standalone helpers for common UI patterns. | Name | Description | | - | - | | [useFilter](/composables/utilities/use-filter) | Filter arrays based on search queries | | [useOverflow](/composables/utilities/use-overflow) | Compute item capacity for responsive truncation | | [usePagination](/composables/utilities/use-pagination) | Pagination state with navigation methods | | [useVirtual](/composables/utilities/use-virtual) | Virtual scrolling for large lists | ## Transformers Value transformation utilities. | Name | Description | | - | - | | [toArray](/composables/transformers/to-array) | Convert any value to an array | | [toReactive](/composables/transformers/to-reactive) | Convert MaybeRef objects to reactive proxies | --- # Use Form URL: https://0.vuetifyjs.com/composables/forms/use-form # useForm A composable for building reactive forms with validation, field registration, and submission handling. Built on top of the registry system for managing form fields. ## Usage The `useForm` composable provides a powerful interface for managing form state, validation, and submission. It extends the registry pattern to handle form-specific requirements like validation rules, error states, and field lifecycle management. ```ts import { useForm } from '@vuetify/v0' const form = useForm() const email = form.register({ id: 'email', value: '', rules: [ (value) => value.includes('@') || 'Must be a valid email', (value) => value.length > 0 || 'Required' ] }) console.log(email.value) // '' console.log(email.errors.value) // [] ``` --- # Use Proxy Model URL: https://0.vuetifyjs.com/composables/forms/use-proxy-model # useProxyModel A composable for syncing refs bidirectionally with selection contexts, enabling seamless v-model integration with selection state. ## Usage The `useProxyModel` composable syncs an existing ref (like from `defineModel()`) with a selection context bidirectionally. Changes in either direction automatically propagate. ```ts import { ref } from 'vue' import { createSelection, useProxyModel } from '@vuetify/v0' const model = ref() const selection = createSelection({ events: true }) selection.onboard([ { id: 'apple', value: 'Apple' }, { id: 'banana', value: 'Banana' }, ]) // Sync model with selection const stop = useProxyModel(selection, model) model.value = 'Apple' console.log(selection.selectedIds) // Set { 'apple' } selection.select('banana') console.log(model.value) // 'Banana' ``` ## With defineModel Perfect for Vue components with v-model: ```vue UseProxyModel {{ item[1].value }} ``` ## Examples ### Multi-select with Array Mutations ```ts const model = ref([]) useProxyModel(selection, model, { multiple: true }) selection.onboard([ { id: 'a', value: 'A' }, { id: 'b', value: 'B' }, ]) // Array mutations sync automatically model.value.push('A') console.log(selection.selectedIds) // Set { 'a' } model.value.splice(0, 1, 'B') console.log(selection.selectedIds) // Set { 'b' } ``` ### Transform Functions ```ts const model = ref() useProxyModel(selection, model, { transformIn: (val) => String(val).toUpperCase(), transformOut: (val) => String(val).toLowerCase(), }) selection.onboard([ { id: '1', value: 'HELLO' }, ]) model.value = 'hello' // Transforms to 'HELLO' before lookup console.log(selection.selectedIds) // Set { '1' } console.log(model.value) // 'hello' (transformed output) ``` ### Late Registration ```ts const model = ref('Item 2') useProxyModel(selection, model) // Item 2 doesn't exist yet, but will be auto-selected when registered setTimeout(() => { selection.register({ id: '2', value: 'Item 2' }) console.log(selection.selectedIds) // Set { '2' } }, 1000) ``` ### Manual Cleanup ```ts const stop = useProxyModel(selection, model) // Later, stop syncing stop() // Changes no longer sync model.value = 'New Value' // selection.selectedIds unchanged ``` --- # Create Context URL: https://0.vuetifyjs.com/composables/foundation/create-context # createContext The `createContext` factory function is at the heart of all functionality in Vuetify0. It is a small wrapper around the Vue 3 [provide](https://vuejs.org/guide/components/provide-inject.html#provide) and [inject](https://vuejs.org/guide/components/provide-inject.html#inject) APIs, allowing you to create a context that can be shared across components. ## Usage ```ts import { shallowRef } from 'vue' import { createContext } from '@vuetify/v0' import type { ShallowRef } from 'vue' interface MyContext { isDisabled: ShallowRef } const [useContext, provideContext] = createContext('namespace') provideContext({ isDisabled: shallowRef(false) }) export { useContext } ``` --- # Create Plugin URL: https://0.vuetifyjs.com/composables/foundation/create-plugin # createPlugin This composable allows you to create a Vue plugin with specific functionality that can be registered and used within your application. ## Usage { id="usage" } Use in conjunction with the [createContext](/composables/foundation/create-context "createContext Documentation") composable to make [Vue plugins](https://vuejs.org/guide/reusability/plugins "Vue Plugins Documentation") that work seemlessly with [Vue Provide / Inject](https://vuejs.org/guide/components/provide-inject "Vue Provide / Inject Documentation"). ```ts import { createContext, createPlugin } from '@vuetify/v0' interface MyPluginContext { app: string } // use is the inject function and provide is the provide function export const [useMyContext, provideMyContext] = createContext('provide-namespace') export function createMyPlugin () { const context = { app: 'my-app' } return createPlugin({ namespace: 'provide-namespace', provide: (app: App) => { provideMyContext(context, app) }, setup: (app: App) => { // For everything else not provide related } }) } ``` > The **setup** and **provide** functions do the same thing, they are separated for semantic purposes. Then, in your main application file, register the plugin like so: ```ts { resource="src/main.ts" } import { createApp } from 'vue' import { createMyPlugin } from './path/to/plugin' const app = createApp(App) app.use(createMyPlugin()) ``` Now, whenever your application starts, the plugin is registered and the context is provided. Use the `useMyContext` function to access this context in any component: ```vue { resource="src/components/MyComponent.vue" } {{ context.app }} ``` --- # Create Trinity URL: https://0.vuetifyjs.com/composables/foundation/create-trinity # createTrinity The **createTrinity** factory function is a type-safe utility for generating a 3-item tuple—called a **trinity**—which contains a context consumer, a provider, and the underlying context object. ## Usage The trinity pattern is the marrying of provide and inject with a context object. It provides a clean and type safe way to create a sharable singleton state. ```ts import { createContext, createTrinity } from '@vuetify/v0' import { ref } from 'vue' interface User { id: string name: string } interface UserContext { user: Ref updateUser: (user: User) => void } function createUserContext() { const [useContext, provideContext] = createContext('user') const user = ref({ id: '123', name: 'John Doe' }) function updateUser(newUser: User) { user.value = newUser } const context: UserContext = { user, updateUser } return createTrinity(useContext, provideContext, context) } export const [useUser, provideUser, defaultUserContext] = createUserContext() ``` --- # Use Breakpoints URL: https://0.vuetifyjs.com/composables/plugins/use-breakpoints # useBreakpoints The `useBreakpoints` composable provides comprehensive responsive design capabilities through reactive viewport dimension detection. It automatically tracks window size changes and provides boolean flags for current breakpoint ranges, enabling mobile-first and responsive layouts. ## Installation First, install the breakpoints plugin in your application: ```ts import { createApp } from 'vue' import { createBreakpointsPlugin } from '@vuetify/v0' import App from './App.vue' const app = createApp(App) app.use( createBreakpointsPlugin({ mobileBreakpoint: 'md', breakpoints: { xs: 0, sm: 600, md: 960, lg: 1280, xl: 1920, xxl: 2560, }, }) ) app.mount('#app') ``` ## Usage Once the plugin is installed, use the `useBreakpoints` composable in any component: ```vue UseBreakpoints Mobile layout active Current breakpoint: {{ breakpoints.name }} Viewport: {{ breakpoints.width }} x {{ breakpoints.height }} ``` --- # Use Features URL: https://0.vuetifyjs.com/composables/plugins/use-features # useFeatures Manage feature flags and simple variations across your app. Register features, toggle them, and query a variation value for A/B-style behavior. ## Usage Install the Features plugin once, then access the context anywhere via `createFeatures`. ```ts import { createApp } from 'vue' import { createFeaturesPlugin } from '@vuetify/v0' import App from './App.vue' const app = createApp(App) app.use( createFeaturesPlugin({ features: { analytics: true, debug_mode: false, notifications: false, search: { $value: true, $variation: 'v2' }, }, }) ) app.mount('#app') ``` Now in any component, access current feature flags and variations: ```vue UseFeatures Analytics: {{ features.get('analytics') }} Debug Mode: {{ features.get('debug_mode') }} Notifications: {{ features.get('notifications') }} Search Variation: {{ features.variation('search', 'v1') }} ``` Optionally register features at runtime: ```vue UseFeatures ``` --- # Use Hydration URL: https://0.vuetifyjs.com/composables/plugins/use-hydration # useHydration The `useHydration` composable provides SSR hydration state management, allowing you to detect when your application has been hydrated on the client side. This is essential for preventing hydration mismatches and controlling when browser-only features should activate. ## Installation First, install the hydration plugin in your application: ```ts import { createApp } from 'vue' import { createHydrationPlugin } from '@vuetify/v0' import App from './App.vue' const app = createApp(App) app.use(createHydrationPlugin()) app.mount('#app') ``` ## Usage Once the plugin is installed, use the `useHydration` composable in any component: ```vue UseHydration Application is hydrated - browser features available Rendering on server or waiting for hydration ``` ## How Hydration Works The hydration plugin automatically detects when the root component has mounted and marks the application as hydrated: 1. During SSR, `isHydrated` is `false` 2. When the root component mounts on the client, the plugin calls `hydrate()` 3. `isHydrated` becomes `true`, signaling that browser APIs are safe to use 4. Components that depend on hydration state can now activate browser-only features The plugin uses a Vue mixin to detect the root component mount: ```ts app.mixin({ mounted() { if (this.$parent !== null) return // Skip child components context.hydrate() // Only hydrate on root mount } }) ``` --- # Use Locale URL: https://0.vuetifyjs.com/composables/plugins/use-locale # useLocale The `useLocale` composable provides comprehensive internationalization (i18n) capabilities, allowing you to manage multiple locales, switch between them dynamically, and translate messages with variable replacement and message linking. Built on `useSingle` for single-locale selection and supports custom adapters for integration with different i18n libraries. ## Installation First, install the locale plugin in your application: ```ts import { createApp } from 'vue' import { createLocalePlugin } from '@vuetify/v0' import App from './App.vue' const app = createApp(App) app.use( createLocalePlugin({ default: 'en', fallback: 'en', messages: { en: { hello: 'Hello', welcome: 'Welcome, {name}!', greeting: 'Hello, {0}! You have {1} messages.', }, es: { hello: 'Hola', welcome: '¡Bienvenido, {name}!', greeting: '¡Hola, {0}! Tienes {1} mensajes.', }, fr: { hello: 'Bonjour', welcome: 'Bienvenue, {name}!', greeting: 'Bonjour, {0}! Vous avez {1} messages.', }, }, }) ) app.mount('#app') ``` ## Usage Once the plugin is installed, use the `useLocale` composable in any component: ```vue UseLocale {{ locale.t('hello') }} {{ locale.t('welcome', { name: 'John' }) }} English Español Français ``` --- # Use Logger URL: https://0.vuetifyjs.com/composables/plugins/use-logger # useLogger The `useLogger` composable provides a flexible, adapter-based logging system for Vue applications. It supports multiple log levels, runtime enable/disable toggling, and integrates seamlessly with popular logging libraries like Consola and Pino through a simple adapter pattern. ## Installation First, install the logger plugin in your application: ```ts import { createApp } from 'vue' import { createLoggerPlugin } from '@vuetify/v0' import App from './App.vue' const app = createApp(App) app.use( createLoggerPlugin({ level: 'info', enabled: true, }) ) app.mount('#app') ``` ## Usage Once the plugin is installed, use the `useLogger` composable in any component: ```vue UseLogger Check the console for logs ``` --- # Use Permissions URL: https://0.vuetifyjs.com/composables/plugins/use-permissions # usePermissions Manage role-based permissions across your app. Register permissions for roles, actions, and subjects with optional context-aware conditions. ## Usage Install the Permissions plugin once, then access the context anywhere via `createPermissions`. ```ts import { createApp } from 'vue' import { createPermissionsPlugin } from '@vuetify/v0' import App from './App.vue' const app = createApp(App) app.use( createPermissionsPlugin({ permissions: { admin: [ [['read', 'write'], 'user', true], [['read', 'write'], 'post', true], ['delete', ['user', 'post'], true], ], editor: [ [['read', 'write'], 'post', true], ['read', 'user', true], ['delete', 'post', (context) => context.isOwner], ], viewer: [ ['read', ['user', 'post'], true], ], }, }) ) app.mount('#app') ``` Now in any component, check permissions for specific roles: ```vue UsePermissions Delete User (Admin Only) Edit Post Delete Own Post ``` Optionally register permissions at runtime: ```vue UsePermissions ``` ## Examples ### Basic RBAC Setup ```ts const permissions = { admin: [ [['create', 'read', 'update', 'delete'], ['user', 'post', 'comment'], true], ], moderator: [ [['read', 'update', 'delete'], ['post', 'comment'], true], ['read', 'user', true], ], user: [ ['read', ['post', 'comment'], true], [['create', 'update'], 'post', (context) => context.userId === context.authorId], ], } ``` ### Context-Aware Permissions ```ts const permissions = { editor: [ ['edit', 'post', (context) => { return context.post.authorId === context.currentUserId || context.currentUser.isAdmin }], ['publish', 'post', (context) => { return context.post.status === 'draft' && context.currentUser.department === 'editorial' }], ], } ``` --- # Use Storage URL: https://0.vuetifyjs.com/composables/plugins/use-storage # useStorage The `useStorage` composable provides reactive storage management with support for multiple storage backends (localStorage, sessionStorage, memory). Built with an adapter pattern for flexibility, it automatically serializes values, manages reactive refs, and provides SSR-safe operations. ## Installation First, install the storage plugin in your application: ```ts import { createApp } from 'vue' import { createStoragePlugin } from '@vuetify/v0' import App from './App.vue' const app = createApp(App) // Use default settings (localStorage in browser, memory in SSR) app.use(createStoragePlugin()) app.mount('#app') ``` ## Usage Once the plugin is installed, use the `useStorage` composable in any component: ```vue UseStorage Welcome, {{ username }}! ``` --- # Use Theme URL: https://0.vuetifyjs.com/composables/plugins/use-theme # useTheme The `useTheme` composable provides comprehensive theme management capabilities, allowing you to register multiple themes, switch between them dynamically, and automatically generate CSS custom properties. Built on `useSingle` for single-theme selection and `useTokens` for design token alias resolution. ## Installation First, install the theme plugin in your application: ```ts import { createApp } from 'vue' import { createThemePlugin } from '@vuetify/v0' import App from './App.vue' const app = createApp(App) app.use( createThemePlugin({ default: 'light', themes: { light: { dark: false, colors: { primary: '#3b82f6', secondary: '#8b5cf6', background: '#ffffff', surface: '#f5f5f5', }, }, dark: { dark: true, colors: { primary: '#60a5fa', secondary: '#a78bfa', background: '#1e293b', surface: '#334155', }, }, }, }) ) app.mount('#app') ``` ## Usage Once the plugin is installed, use the `useTheme` composable in any component: ```vue UseTheme Current Theme: {{ theme.selectedId }} Dark mode: {{ theme.isDark ? 'enabled' : 'disabled' }} Toggle Theme ``` ## Adapter Pattern The adapter pattern allows you to customize how theme colors are transformed into CSS variables. This provides flexibility for different styling strategies and frameworks. ### Default Adapter By default, `useTheme` uses `Vuetify0ThemeAdapter`, which: - Generates CSS custom properties (e.g., `--v0-primary: #3b82f6`) - Injects them into a `
Mobile layout active
Current breakpoint: {{ breakpoints.name }}
Viewport: {{ breakpoints.width }} x {{ breakpoints.height }}
Analytics: {{ features.get('analytics') }}
Debug Mode: {{ features.get('debug_mode') }}
Notifications: {{ features.get('notifications') }}
Search Variation: {{ features.variation('search', 'v1') }}
Application is hydrated - browser features available
Rendering on server or waiting for hydration
{{ locale.t('welcome', { name: 'John' }) }}
Dark mode: {{ theme.isDark ? 'enabled' : 'disabled' }}