git clone https://github.com/NeverSight/learn-skills.dev
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/agents-inc/skills/web-ui-vuetify" ~/.claude/skills/neversight-learn-skills-dev-web-ui-vuetify && rm -rf "$T"
data/skills-md/agents-inc/skills/web-ui-vuetify/SKILL.mdVuetify Patterns
Quick Guide: Vuetify provides 80+ pre-styled Vue 3 components implementing Material Design. Configure with
-- setcreateVuetify()for colors,themefor global component props, anddefaultsfor MD3 compliance. Useblueprint: md3for scoped prop overrides. Customize at the SASS level withv-defaults-providerfor compile-time changes. Current: v3.8.x --@use 'vuetify/settings'composable for form validation, date picker improvements, performance optimizations. Vuetify is template-driven -- prefer declarative props/slots over imperative JS.useRules
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
, named constants)import type
(You MUST install and register Vuetify as a Vue plugin via
-- components will not render without the plugin)app.use(vuetify)
(You MUST use the
system in defaults
or createVuetify()
for consistent component props -- never repeat the same prop on every instance)v-defaults-provider
(You MUST use named slots (
, v-slot:item.<key>
) for component customization -- not wrapper divs with manual styling)v-slot:prepend
(You MUST define
headers and column arrays outside the component -- inline arrays cause re-renders on every parent update)v-data-table
</critical_requirements>
Auto-detection: Vuetify, vuetify, createVuetify, v-btn, v-card, v-data-table, v-text-field, v-select, v-autocomplete, v-dialog, v-navigation-drawer, v-app-bar, v-toolbar, v-chip, v-snackbar, v-form, v-defaults-provider, v-theme-provider, useTheme, useDisplay, useDate, useRules, mdi, @mdi/font, vuetify/blueprints, vuetify/settings, vuetify/styles
When to use:
- Building Vue 3 applications with pre-styled Material Design components
- Rapid development of admin dashboards, data tables, and CRUD interfaces
- Projects that benefit from a comprehensive defaults/theming system
- Applications requiring built-in accessibility, RTL, and i18n support
When NOT to use:
- Non-Vue projects (Vuetify is Vue-only)
- Projects requiring a non-Material Design aesthetic without heavy customization
- Minimal bundle size is critical and only a few components are needed (consider a headless library)
- Projects using Vue 2 (Vuetify 3 requires Vue 3)
Key patterns covered:
- Plugin setup with
, themes, and blueprintscreateVuetify() - Global defaults and scoped defaults with
v-defaults-provider - SASS variable customization at compile time
- Slot-based component customization
- Data tables with sorting, filtering, pagination, and custom column rendering
- Form validation with rules arrays and
composableuseRules - Theme toggling and
/useTheme
composablesuseDisplay
Detailed Resources:
- examples/core.md -- Plugin setup, theming, defaults, SASS variables, blueprints, TypeScript, composables
- examples/data-tables.md -- v-data-table headers, slots, sorting, filtering, pagination, server-side
- examples/forms.md -- v-form, validation rules, useRules, input components, custom validation
- examples/layout.md -- v-app, v-app-bar, v-navigation-drawer, v-container/v-row/v-col grid, responsive patterns
- reference.md -- Decision frameworks, component quick reference, anti-patterns
<philosophy>
Philosophy
Vuetify is an opinionated, batteries-included component library. Unlike headless libraries, Vuetify ships styled components with a complete design system. Its power comes from three layers of customization:
- Theme layer -- colors, dark mode, typography via
. All components inherit automatically.createVuetify({ theme }) - Defaults layer -- global or scoped prop values via
config ordefaults
. Change everyv-defaults-provider
tov-btn
in one line.variant="outlined" - SASS layer -- compile-time variable overrides via
. Changes border-radius, heights, font families at the CSS level.@use 'vuetify/settings'
Vuetify 3.x key features:
- Full Vue 3 Composition API support
- Tree-shakeable components via
/vuetify-loadervite-plugin-vuetify - Material Design 3 blueprint (
) for MD3 compliancemd3 - Built-in composables:
,useTheme
,useDisplay
,useDateuseRules
for scoped prop cascading (unique to Vuetify)v-defaults-provider- Extensive slot system for deep component customization without CSS hacks
- Built-in i18n, RTL, and accessibility
<patterns>
Core Patterns
Pattern 1: Plugin Setup
Every Vuetify app requires
createVuetify() and app.use(). The plugin provides the theme, defaults, icons, and locale to all components.
import { createApp } from "vue"; import { createVuetify } from "vuetify"; const vuetify = createVuetify({ theme: { defaultTheme: "light", themes: { light: { colors: { primary: "#1867C0", secondary: "#5CBBF6", }, }, }, }, defaults: { VBtn: { variant: "flat", rounded: "lg" }, VTextField: { variant: "outlined", density: "comfortable" }, VCard: { elevation: 2, rounded: "lg" }, }, }); const app = createApp(App); app.use(vuetify); app.mount("#app");
Why good: single configuration point, all components inherit theme and default props, tree-shakeable
For blueprints, SASS variables, TypeScript augmentation, and SSR setup, see examples/core.md.
Pattern 2: Theming and Dark Mode
Define multiple themes in
createVuetify and toggle with useTheme. Vuetify generates CSS variables for each theme.
<script setup> import { useTheme } from "vuetify"; const theme = useTheme(); function toggleTheme() { theme.global.name.value = theme.global.current.value.dark ? "light" : "dark"; } </script> <template> <v-btn @click="toggleTheme" icon="mdi-brightness-6" /> </template>
Why good: reactive theme switching, CSS variables prevent flash of wrong theme, custom themes can extend built-in ones
For custom theme creation and color variations, see examples/core.md.
Pattern 3: Global Defaults and Scoped Defaults
The
defaults system is Vuetify's most powerful consistency tool. Set props globally in createVuetify() or scope them with v-defaults-provider.
<template> <!-- All buttons inside this provider get these defaults --> <v-defaults-provider :defaults="{ VBtn: { color: 'secondary', variant: 'tonal' }, VCard: { elevation: 0, border: true }, }" > <v-card> <v-card-text> <!-- This button is tonal + secondary without explicit props --> <v-btn>Scoped Default</v-btn> </v-card-text> </v-card> </v-defaults-provider> </template>
Why good: eliminates prop repetition, section-specific styling without CSS, nests and cascades like CSS scoping
Pattern 4: Slot-Based Component Customization
Vuetify components expose named slots for every internal element. Use
v-slot to replace or augment internal rendering.
<template> <v-text-field label="Amount" type="number"> <template v-slot:prepend-inner> <v-icon>mdi-currency-usd</v-icon> </template> <template v-slot:append-inner> <v-chip size="x-small" color="success">.00</v-chip> </template> </v-text-field> </template>
Why good: customizes internal elements without CSS overrides, type-safe slot props, preserves component behavior
For data table column slots, see examples/data-tables.md.
Pattern 5: Data Table Overview
v-data-table handles sorting, pagination, filtering, and selection. Define headers outside the template, use v-slot:item.<key> for custom column rendering.
<script setup> const headers = [ { title: "Name", key: "name" }, { title: "Status", key: "status" }, { title: "Actions", key: "actions", sortable: false }, ]; </script> <template> <v-data-table :items="items" :headers="headers"> <template v-slot:item.status="{ item }"> <v-chip :color="item.status === 'active' ? 'success' : 'error'" size="small" > {{ item.status }} </v-chip> </template> <template v-slot:item.actions="{ item }"> <v-icon size="small" @click="edit(item)">mdi-pencil</v-icon> </template> </v-data-table> </template>
Why good: headers array is stable (no re-render), slot customization per column, built-in sort/filter/paginate
For server-side data tables, expandable rows, and search, see examples/data-tables.md.
Pattern 6: Form Validation
Vuetify forms use
:rules arrays on inputs. Each rule is a function returning true or an error string. The useRules composable (v3.8+) provides common validators.
<script setup> import { ref } from "vue"; import { useRules } from "vuetify/labs/rules"; const form = ref(null); const email = ref(""); const rules = useRules(); async function submit(event: SubmitEvent) { const { valid } = await form.value.validate(); if (!valid) return; // proceed with submission } </script> <template> <v-form ref="form" validate-on="submit" @submit.prevent="submit"> <v-text-field v-model="email" label="Email" :rules="[rules.required(), rules.email()]" /> <v-btn type="submit" color="primary">Submit</v-btn> </v-form> </template>
Why good: declarative validation,
useRules eliminates boilerplate rule functions, validate-on controls timing
For custom rules, multi-field validation, and input types, see examples/forms.md.
Pattern 7: Responsive Design with useDisplay
The
useDisplay composable provides reactive breakpoint state. Use it for logic-driven responsive behavior (template responsiveness uses Vuetify's grid props).
<script setup> import { useDisplay } from "vuetify"; const { mobile, mdAndUp, name } = useDisplay(); </script> <template> <v-navigation-drawer v-if="mdAndUp" permanent /> <v-navigation-drawer v-else v-model="drawer" temporary /> <v-app-bar :density="mobile ? 'compact' : 'default'" /> </template>
Why good: reactive breakpoint booleans, avoids CSS media query duplication in script, matches Vuetify's breakpoint system
</patterns><performance>
Performance
Tree-Shaking
Use
vite-plugin-vuetify (Vite) or webpack-plugin-vuetify (Webpack) for automatic tree-shaking. Only imported components are bundled.
// vite.config.ts import vuetify from "vite-plugin-vuetify"; export default { plugins: [vuetify({ autoImport: true })], };
Without the plugin, import
vuetify/components and vuetify/directives to include everything (larger bundle).
Stable References
// GOOD: headers defined outside component const headers: DataTableHeader[] = [{ title: "Name", key: "name" }]; // BAD: inline array recreated every render // <v-data-table :headers="[{ title: 'Name', key: 'name' }]" />
SASS Variable Customization vs Runtime Theming
</performance>Need to change at runtime? --> Use theme colors in createVuetify() Need compile-time CSS changes? --> Use SASS variables (@use 'vuetify/settings') Need per-section prop defaults? --> Use v-defaults-provider (zero CSS cost)
<red_flags>
RED FLAGS
High Priority Issues:
- Missing
-- components render as empty custom elements without the pluginapp.use(vuetify) - Inline header/column arrays on
-- causes full table re-render on every parent updatev-data-table - Using
in Composition API -- usethis.$vuetify
,useTheme()
,useDisplay()
composables insteaduseDate() - Importing
andvuetify/components
whenvuetify/directives
is available -- negates tree-shakingvite-plugin-vuetify
Medium Priority Issues:
- Setting
,variant
,density
on every component instance -- userounded
indefaults
insteadcreateVuetify() - Wrapping Vuetify components in extra divs for styling -- use the component's own props/slots/classes
- Hardcoding colors (
) in templates instead of using theme colors (#1867C0
)color="primary" - Using
to toggle dialogs/drawers instead ofv-if
-- loses transition animations and internal statev-model
Common Mistakes:
- Forgetting
when not using the build plugin -- no styles load at allimport 'vuetify/styles' - Using CSS
to override Vuetify styles -- use SASS variables or!important
prop with higher specificityclass - Not setting
onvalidate-on
-- defaults tov-form
which validates on every keystroke (useinput
or"submit"
for better UX)"blur lazy" - Placing
without a parentv-col
insidev-row
-- grid system requires the full nestingv-container
Gotchas & Edge Cases:
acceptsdensity
,"default"
,"comfortable"
-- it is NOT a numeric value"compact"
is a separate component for server-side pagination -- do not usev-data-table-server
with manual paginationv-data-table
cascades -- nested providers merge with parent, later values winv-defaults-provider
breakpoints differ from CSS breakpoints in default config (xs: 0, sm: 600, md: 960, lg: 1280, xl: 1920, xxl: 2560)useDisplay- Theme
generatevariations
and-lighten-N
color variants automatically -- do not manually define them-darken-N
onv-model
controls visibility -- do not usev-dialog
prop (Vue 3value
replacesv-model
).sync- SASS variables require the Vite/Webpack plugin with
pointing to your settings filestyles.configFile
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
, named constants)import type
(You MUST install and register Vuetify as a Vue plugin via
-- components will not render without the plugin)app.use(vuetify)
(You MUST use the
system in defaults
or createVuetify()
for consistent component props -- never repeat the same prop on every instance)v-defaults-provider
(You MUST use named slots (
, v-slot:item.<key>
) for component customization -- not wrapper divs with manual styling)v-slot:prepend
(You MUST define
headers and column arrays outside the component -- inline arrays cause re-renders on every parent update)v-data-table
Failure to follow these rules will produce unstyled components, unnecessary re-renders, and inconsistent UI.
</critical_reminders>