Vuetify is a Vue 3 Material Design component framework. It ships more than one
hundred ready-made UI components — buttons, forms, data tables, dialogs, navigation,
pickers, and more — together with a reactive system for theming, responsive breakpoints,
icons, internationalization, and global configuration. You write Vue templates with
components like <v-btn> and <v-text-field>, and Vuetify handles the styling,
accessibility scaffolding, and Material Design behavior.
This guide covers everything you need to install Vuetify, configure it, and use its
components and features in a real application. It is built from the framework's source
code at version 4.1.0, so every option, breakpoint, color, and command below reflects
what the code actually does.
Note
Vuetify 4 requires Vue 3.5.0 or later and Node.js 24.11.1+ for local development tooling. It is published under the MIT license.
Table of Contents
- Quick Start
- Installation
- Core Concepts
- The Application Layout
- Theme & Colors
- Display & Breakpoints
- Icons
- Global Defaults
- Blueprints
- Internationalization & RTL
- Dates
- The Component Catalog
- Forms & Validation
- Directives
- Programmatic Scrolling
- Styling, Utility Classes & SASS
- Tree-Shaking & Production Builds
- Server-Side Rendering (SSR)
- Troubleshooting
- Frequently Asked Questions
- Reference
1. Quick Start
The fastest path to a running Vuetify app is the official scaffolding tool, create-vuetify. It generates a complete Vite + Vue 3 + Vuetify project with sensible defaults.
pnpm
pnpm create vuetify
npm
npm create vuetify@latest
yarn
yarn create vuetify
bun
bun create vuetify
The tool prompts you for a project name, whether to use TypeScript, and which package manager to install with:
success Installed "create-vuetify@x.x.x" with binaries:
- create-vuetify
? Project name: ❯ vuetify-project
? Use TypeScript?: ❯ No / Yes
? Would you like to install dependencies with yarn, npm, or pnpm?:
❯ yarn
npm
pnpm
bun
none
Once the scaffold finishes, start the dev server:
cd vuetify-project
pnpm dev
VITE v7.x ready in 412 ms
➜ Local: http://localhost:3000/
➜ Network: use --host to expose
Open the printed URL and you have a working Vuetify application. Every component, the theme system, and the icon set are already wired up.
Tip
create-vuetifyis the recommended starting point for new projects. If you are adding Vuetify to an existing project, follow the manual installation steps instead.
2. Installation
Vuetify supports many environments. Pick the one that matches your project.
Using Vite (recommended)
Use create-vuetify as shown in the Quick Start. This sets up Vite, the
vite-plugin-vuetify for automatic tree-shaking, fonts, and icons for you.
Using Nuxt 3
You can either use the dedicated module or wire Vuetify in manually.
Manual setup. Create a Nuxt app, then add Vuetify and the Vite plugin as dev dependencies plus an icon font:
pnpm
pnpx nuxi@latest init <project-name>
cd <project-name>
# Create a .npmrc file with shamefully-hoist=true
pnpm install
pnpm i -D vuetify vite-plugin-vuetify
pnpm i @mdi/font
npm
npx nuxi@latest init <project-name>
cd <project-name>
npm install
npm i -D vuetify vite-plugin-vuetify
npm i @mdi/font
Add the plugin to nuxt.config.ts:
import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
export default defineNuxtConfig({
build: {
transpile: ['vuetify'],
},
vite: {
plugins: [
// @ts-expect-error
vuetify({ autoImport: true }),
],
vue: {
template: {
transformAssetUrls,
},
},
},
})
Initialize Vuetify as a Nuxt plugin in ~/plugins/vuetify.ts:
import '@mdi/font/css/materialdesignicons.css'
import 'vuetify/styles'
import { createVuetify } from 'vuetify'
export default defineNuxtPlugin((app) => {
const vuetify = createVuetify({
// ... your configuration
})
app.vueApp.use(vuetify)
})
Finally, wrap your app in the root <v-app> component in ~/app.vue or
~/layouts/default.vue:
<template>
<NuxtLayout>
<v-app>
<NuxtPage />
</v-app>
</NuxtLayout>
</template>
Tip
The opinionated vuetify-nuxt-module (Vite only) works out of the box with zero configuration for most use cases.
Using Laravel
As long as you chain .use() on your Vue app, Vuetify registers correctly. In
resources/js/app.ts:
import { createApp } from 'vue'
import '@mdi/font/css/materialdesignicons.css'
import 'vuetify/styles'
import { createVuetify } from 'vuetify'
const app = createApp()
const vuetify = createVuetify()
app.use(vuetify).mount('#app')
And add the Vite plugin to vite.config.ts:
import vuetify from 'vite-plugin-vuetify'
export default defineConfig({
plugins: [
vuetify({ autoImport: true }),
],
})
Warning
If a font is defined in
resources/views/app.blade.php, Vuetify's own font settings will not take effect.
Using a CDN
For quick prototypes with no build tooling, load the precompiled CSS and JS bundles from jsDelivr. All components and styles are included.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vuetify@4.1.0/dist/vuetify.min.css" />
</head>
<body>
<div id="app">
<v-app>
<v-container>
<v-text-field label="My field" />
</v-container>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@4.1.0/dist/vuetify.min.js"></script>
<script>
const { createApp } = Vue
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const app = createApp()
app.use(vuetify).mount('#app')
</script>
</body>
</html>
Using an ES Module with a CDN
With an import map
you can use Vuetify as native ES modules without a bundler. Note the bundle is
vuetify.esm.js, not .min.js:
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vuetify@4.1.0/dist/vuetify.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css" />
<link rel="stylesheet" href="https://fonts.bunny.net/css?family=roboto:400,500,700" />
<script type="importmap">
{
"imports": {
"vue": "https://cdn.jsdelivr.net/npm/vue@latest/dist/vue.esm-browser.js",
"vuetify": "https://cdn.jsdelivr.net/npm/vuetify@4.1.0/dist/vuetify.esm.js"
}
}
</script>
</head>
Using VitePress
After initializing a VitePress project, add Vuetify and register it in
.vitepress/theme/index.ts:
import DefaultTheme from 'vitepress/theme'
import 'vuetify/styles'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
import { createVuetify } from 'vuetify'
const vuetify = createVuetify({ components, directives })
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.use(vuetify)
},
}
Existing projects
To add Vuetify to an existing project without scaffolding, install the package:
pnpm
pnpm i vuetify
npm
npm i vuetify
yarn
yarn add vuetify
bun
bun add vuetify
Then register it where you create the Vue app:
import { createApp } from 'vue'
import 'vuetify/styles'
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
import App from './App.vue'
const vuetify = createVuetify({
components,
directives,
})
createApp(App).use(vuetify).mount('#app')
Important
Importing all components and directives like this is convenient but increases your bundle size during development. For production, prefer the Vite plugin with
autoImportso only the components you actually use are bundled.
Fonts
Vuetify uses Roboto as its default font. You must add it yourself. The recommended
approach uses @fontsource/roboto:
pnpm
pnpm i @fontsource/roboto
npm
npm i @fontsource/roboto
Then import the weights you need in main.ts:
import '@fontsource/roboto/100.css'
import '@fontsource/roboto/300.css'
import '@fontsource/roboto/400.css'
import '@fontsource/roboto/500.css'
import '@fontsource/roboto/700.css'
import '@fontsource/roboto/900.css'
A cleaner alternative bundles fonts with unplugin-fonts so they are downloaded once at
build time. See the Reference for the vite.config.ts snippet.
3. Core Concepts
The createVuetify plugin
Everything starts with createVuetify(). It returns a Vue plugin that you register with
app.use(). Internally, it builds the framework's reactive subsystems and provides them to
every component in your app:
import { createVuetify } from 'vuetify'
const vuetify = createVuetify({
theme: { /* ... */ }, // colors, light/dark
display: { /* ... */ }, // responsive breakpoints
icons: { /* ... */ }, // icon sets and aliases
locale: { /* ... */ }, // translations and RTL
date: { /* ... */ }, // date adapter for pickers
defaults:{ /* ... */ }, // global prop defaults
blueprint: /* ... */, // a preset bundle of the above
ssr: true, // enable when server-rendering
})
When the plugin installs, it registers your components and directives globally and exposes
a single $vuetify object on every component instance, with defaults, display,
theme, icons, locale, and date.
Note
The full list of options is
aliases,blueprint,components,date,directives,defaults,display,goTo,theme,icons,locale, andssr. Each one is described in its own section of this guide and summarized in the Admin Guide configuration reference.
Accessing framework state in your components
Every subsystem has a composable you can call from <script setup>. They return reactive
objects:
<script setup>
import { useTheme } from 'vuetify'
import { useDisplay } from 'vuetify'
const theme = useTheme()
const { mobile, mdAndUp } = useDisplay()
</script>
Caution
These composables must be called during a component's
setup(). CallinguseTheme()oruseDisplay()outside of a component (for example, in a plain module) throws "Could not find Vuetify … injection" because the injection context does not exist there.
4. The Application Layout
Vuetify provides a coordinated layout system. The root component is <v-app>; every
other layout component registers itself with it so that app bars, drawers, and footers
automatically reserve space and never overlap your main content.
| Component | Role |
|---|---|
<v-app> |
Required root. Wraps the whole UI and hosts theme + layout state. |
<v-main> |
The content area that flexes around bars and drawers. |
<v-app-bar> |
Top (or bottom) application toolbar. |
<v-system-bar> |
Slim status bar above the app bar. |
<v-navigation-drawer> |
Side navigation panel (left/right, permanent/temporary). |
<v-footer> |
Bottom footer; can be made app to integrate with the layout. |
<v-layout> / <v-layout-item> |
Lower-level building blocks for custom layouts. |
A typical shell looks like this:
<template>
<v-app>
<v-app-bar title="My Application" />
<v-navigation-drawer>
<v-list>
<v-list-item title="Dashboard" to="/" />
<v-list-item title="Settings" to="/settings" />
</v-list>
</v-navigation-drawer>
<v-main>
<v-container>
<router-view />
</v-container>
</v-main>
<v-footer app>© 2026 My Company</v-footer>
</v-app>
</template>
Important
Wrap your application in exactly one
<v-app>. Many components — overlays, menus, dialogs, theming — depend on the providers that<v-app>and thecreateVuetifyplugin set up. Without it, layout offsets and theme classes will not apply correctly.
Tip
Put page content inside
<v-container>(centered, responsive gutters), then use<v-row>and<v-col>for the 12-column grid. See The Component Catalog → Grid & Layout.
5. Theme & Colors
Vuetify ships a complete theming engine. It generates CSS custom properties for a palette and exposes a reactive API to switch themes at runtime.
Built-in themes
Two themes are defined out of the box: light and dark. The default theme is
system, which automatically follows the operating system's
prefers-color-scheme setting and switches live when the user changes it.
const vuetify = createVuetify({
theme: {
defaultTheme: 'dark', // 'light' | 'dark' | 'system' | your custom name
},
})
The default light and dark palettes (exact values from source) include:
| Token | Light | Dark |
|---|---|---|
background |
#FFFFFF |
#121212 |
surface |
#FFFFFF |
#212121 |
primary |
#1867C0 |
#2196F3 |
secondary |
#48A9A6 |
#54B6B2 |
success |
#4CAF50 |
#4CAF50 |
info |
#2196F3 |
#2196F3 |
warning |
#FB8C00 |
#FB8C00 |
error |
#B00020 |
#CF6679 |
Defining a custom theme
const vuetify = createVuetify({
theme: {
defaultTheme: 'myTheme',
themes: {
myTheme: {
dark: false,
colors: {
primary: '#1976D2',
secondary: '#424242',
surface: '#FFFFFF',
background: '#F5F5F5',
error: '#FF5252',
},
},
},
},
})
Each color you declare produces matching utility classes (bg-primary, text-primary,
border-primary) and a contrasting on- color (for example on-primary) is computed
automatically based on luminance so text stays legible on any background.
Tip
You do not have to define
on-primary,on-surface, etc. Vuetify derives them for you using the theme'stheme-on-light/theme-on-darkvariables. Override one only if you want a specific contrast color.
Switching themes at runtime
useTheme() returns the theme instance with these methods:
<script setup>
import { useTheme } from 'vuetify'
const theme = useTheme()
function toggle () {
theme.toggle() // swap between ['light', 'dark']
}
function goDark () {
theme.change('dark') // switch to a named theme
}
function next () {
theme.cycle(['light', 'dark', 'myTheme']) // rotate through a list
}
</script>
<template>
<v-btn @click="toggle">Toggle theme</v-btn>
<div>Current theme: {{ theme.name.value }}</div>
</template>
| Method | Behavior |
|---|---|
change(name, transition?) |
Switch to a named theme. Warns if the name is unknown. |
toggle(['a','b']?, transition?) |
Cycle between two themes (defaults to light/dark). |
cycle([names]?, transition?) |
Advance to the next theme in a list, wrapping around. |
setTransitionOrigin(event) |
Set the origin point for the animated theme transition. |
Useful reactive properties on the instance include name (current theme name), current
(the resolved theme definition), themes (all definitions), isSystem (whether following
the OS), and global.name.
Animated theme transitions
Set transition: true to animate theme changes with a circular reveal using the View
Transitions API (where the browser supports it). You can also pass per-change options:
createVuetify({
theme: {
transition: { duration: '500ms' },
},
})
theme.setTransitionOrigin(pointerEvent) // start the reveal where the user clicked
theme.toggle(undefined, true)
Note
The transition automatically respects the user's
prefers-reduced-motionsetting and falls back to an instant switch when reduced motion is requested or the browser lacksdocument.startViewTransition.
Color variations
You can auto-generate lighter and darker shades of named colors:
createVuetify({
theme: {
variations: {
colors: ['primary', 'secondary'],
lighten: 3, // generates primary-lighten-1..3
darken: 2, // generates primary-darken-1..2
},
},
})
Content Security Policy
If your site uses a CSP nonce, pass it so the injected <style> element is allowed:
createVuetify({ theme: { cspNonce: 'your-nonce' } })
6. Display & Breakpoints
The display system makes your UI responsive. It tracks viewport size and exposes reactive booleans for each breakpoint.
Breakpoint thresholds
The default thresholds (in pixels, from source) are:
| Breakpoint | Range |
|---|---|
xs |
0 – 599 |
sm |
600 – 839 |
md |
840 – 1144 |
lg |
1145 – 1544 |
xl |
1545 – 2137 |
xxl |
2138+ |
Warning
These thresholds changed in Vuetify 4. If you are migrating from Vuetify 3, the
md/lgboundaries are different, so layouts that keyed off the old values may shift. Override them viadisplay.thresholdsif you need the old behavior.
Using useDisplay
<script setup>
import { useDisplay } from 'vuetify'
const { mobile, name, width, height, mdAndUp, smAndDown, platform } = useDisplay()
</script>
<template>
<v-navigation-drawer :permanent="mdAndUp" :temporary="smAndDown" />
<div v-if="mobile">Compact layout</div>
<div>Current breakpoint: {{ name }} ({{ width }}×{{ height }})</div>
</template>
Available reactive members:
- Exact:
xs,sm,md,lg,xl,xxl - And up:
smAndUp,mdAndUp,lgAndUp,xlAndUp - And down:
smAndDown,mdAndDown,lgAndDown,xlAndDown - Values:
name,width,height,mobile,mobileBreakpoint,thresholds - Platform:
platform.android,platform.ios,platform.mac,platform.win,platform.linux,platform.chrome,platform.firefox,platform.touch, and more.
The mobile breakpoint
mobile is true when the viewport is narrower than the mobile breakpoint, which
defaults to 'lg' (1145px). Many components change behavior below it (for example dialogs
become fullscreen). Configure it globally or per component:
createVuetify({
display: {
mobileBreakpoint: 'sm',
thresholds: { sm: 480, md: 768, lg: 1024, xl: 1440, xxl: 1920 },
},
})
<v-data-table :mobile-breakpoint="600" />
7. Icons
Vuetify's icon system is pluggable. A component never hard-codes an SVG path; it asks for an icon value which is resolved through the configured icon set.
Choosing an icon set
The default set is Material Design Icons (mdi), using the font version. Fifteen sets
ship in the box: mdi, mdi-svg, md, fa (Font Awesome font), fa-svg, fa4, fa6,
lucide, tabler, carbon, ph (Phosphor), ri (Remix), bx (BoxIcons), ms, and
mdi-unocss.
import { aliases, mdi } from 'vuetify/iconsets/mdi'
createVuetify({
icons: {
defaultSet: 'mdi',
aliases,
sets: { mdi },
},
})
For the tree-shakeable SVG version of Material Design Icons:
import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'
createVuetify({
icons: { defaultSet: 'mdi', aliases, sets: { mdi } },
})
Using icons
The <v-icon> component and any *-icon prop accept an icon value in several forms:
<!-- From the default set, by name -->
<v-icon icon="mdi-account" />
<!-- A built-in semantic alias (prefixed with $) -->
<v-icon icon="$success" />
<!-- Explicitly from a named set using set:name syntax -->
<v-icon icon="fa:fas fa-home" />
<!-- A raw SVG path string -->
<v-icon icon="svg:M12,2C17.5..." />
Note
Aliases starting with
$map to Vuetify's semantic icons — the icons components use internally for states like checkboxes, sorting, navigation, and ratings. Overriding an alias (for example$success) re-skins every component that uses it. The full alias list includescomplete,cancel,close,delete,clear,success,info,warning,error,prev,next,checkboxOn,checkboxOff,checkboxIndeterminate,sortAsc,sortDesc,expand,collapse,menu,dropdown,radioOn,radioOff,edit,ratingEmpty,ratingFull,ratingHalf,loading,first,last,file,plus,minus,calendar, and more.
Customizing aliases
import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'
import { mdiHome } from '@mdi/js'
createVuetify({
icons: {
defaultSet: 'mdi',
aliases: { ...aliases, customHome: mdiHome },
sets: { mdi },
},
})
<v-icon icon="$customHome" />
8. Global Defaults
The defaults system lets you set default prop values for any component, globally or for a scoped subtree. This is how you enforce a consistent look without repeating props on every element.
Global defaults
createVuetify({
defaults: {
global: {
ripple: false, // applies to every component that has a ripple prop
},
VBtn: {
variant: 'flat',
color: 'primary',
rounded: 'lg',
},
VTextField: {
variant: 'outlined',
density: 'comfortable',
},
},
})
Now every <v-btn> is flat and primary-colored unless you override it inline. Resolution
order is: prop set on the element → component default (VBtn) → global default
(global) → the component's own built-in default.
Scoped defaults with <v-defaults-provider>
To apply defaults to only part of the tree, wrap that part in <v-defaults-provider>:
<v-defaults-provider :defaults="{ VBtn: { size: 'small', variant: 'text' } }">
<v-btn>Small text button</v-btn>
<v-btn>Another one</v-btn>
</v-defaults-provider>
Tip
Defaults can also target nested sub-components. A key like
VBtninside another component's defaults applies only to buttons rendered by that component, which is how you theme, for example, the buttons inside aVCard.
9. Blueprints
A blueprint is a pre-packaged bundle of createVuetify options — themes, defaults,
icons — that gives your whole app a consistent design language in one line. Three Material
Design blueprints ship in the box: md1, md2, and md3 (Material Design 1, 2, and 3).
import { createVuetify } from 'vuetify'
import { md3 } from 'vuetify/blueprints/md3'
const vuetify = createVuetify({
blueprint: md3,
})
Your own options are merged on top of the blueprint, so you can adopt the Material 3 look and still override individual colors or defaults:
const vuetify = createVuetify({
blueprint: md3,
theme: {
themes: { light: { colors: { primary: '#6750A4' } } },
},
})
10. Internationalization & RTL
Vuetify components display some text of their own (pagination labels, data-table messages, "no data" strings, etc.). The locale system controls that text and supports right-to-left layouts.
Built-in languages
Translations ship for 43 languages, including English (en, the fallback), German
(de), Spanish (es), French (fr), Chinese (zh-Hans / zh-Hant), Japanese (ja),
Korean (ko), Arabic (ar), Hebrew (he), Persian (fa), Russian (ru), Portuguese
(pt), Italian (it), and many more.
import { createVuetify } from 'vuetify'
import { en, de, es } from 'vuetify/locale'
const vuetify = createVuetify({
locale: {
locale: 'de',
fallback: 'en',
messages: { en, de, es },
},
})
You can also set the decimal and grouping separators used by number formatting:
createVuetify({
locale: { decimalSeparator: ',', numericGroupSeparator: '.' },
})
Switching locale at runtime
<script setup>
import { useLocale } from 'vuetify'
const { current } = useLocale()
function setGerman () { current.value = 'de' }
</script>
Integrating with vue-i18n
If your app already uses vue-i18n, plug it into Vuetify with the supplied adapter so both share one source of translations:
import { createI18n, useI18n } from 'vue-i18n'
import { createVueI18nAdapter } from 'vuetify/locale/adapters/vue-i18n'
import { en, de } from 'vuetify/locale'
const i18n = createI18n({
legacy: false,
locale: 'en',
messages: { en: { $vuetify: en }, de: { $vuetify: de } },
})
const vuetify = createVuetify({
locale: {
adapter: createVueI18nAdapter({ i18n, useI18n }),
},
})
Right-to-left (RTL)
The languages ar (Arabic), fa (Persian), and he (Hebrew) are RTL by default. Enable
RTL globally or scope it to part of the page with <v-locale-provider>:
<v-locale-provider rtl>
<!-- this subtree renders right-to-left -->
</v-locale-provider>
Use useRtl() to read or react to the current direction:
<script setup>
import { useRtl } from 'vuetify'
const { isRtl } = useRtl()
</script>
11. Dates
The date-based components — <v-date-picker>, <v-date-input>, <v-calendar> — work
through a date adapter so you can use whichever date library your project already
depends on. The built-in VuetifyDateAdapter uses the native Intl API and needs no extra
dependency.
import { createVuetify } from 'vuetify'
// Uses the built-in adapter — nothing else required
const vuetify = createVuetify()
To use date-fns, luxon, dayjs, or moment, install the matching @date-io adapter
and pass it:
import { createVuetify } from 'vuetify'
import DateFnsAdapter from '@date-io/date-fns'
import enUS from 'date-fns/locale/en-US'
const vuetify = createVuetify({
date: {
adapter: DateFnsAdapter,
locale: { en: enUS },
},
})
Note
Vuetify maps its locale codes to standard locale strings for dates automatically (for example
en→en-US,de→de-DE,ja→ja-JP), so date formatting follows the active language out of the box.
12. The Component Catalog
Vuetify ships 104 stable components plus a set of experimental labs components.
Every component is registered globally by createVuetify, so you can use them in any
template without importing them (with the Vite plugin handling tree-shaking). Components are
written in PascalCase (VTextField) and used in kebab-case in templates (<v-text-field>).
Important
Each component exposes a large, strongly-typed set of props, slots, and events. The exhaustive, always-current per-prop API reference is generated directly from the source types and published at vuetifyjs.com. This guide groups the components by purpose so you can find the right one; consult the online API for the full prop signature of any individual component.
Layout & Application
| Component | Purpose |
|---|---|
v-app |
Required root wrapper. |
v-main |
Main content region that flexes around bars/drawers. |
v-app-bar |
Top/bottom application toolbar. |
v-system-bar |
Slim status bar. |
v-toolbar |
Generic toolbar (used standalone or inside bars). |
v-navigation-drawer |
Side navigation panel. |
v-footer |
Page footer. |
v-layout / v-layout-item |
Low-level layout primitives. |
Grid & Layout
| Component | Purpose |
|---|---|
v-container |
Centered, responsive content wrapper. |
v-row / v-col |
12-column responsive flexbox grid. |
v-spacer |
Flexible space inside flex containers. |
v-responsive |
Enforces an aspect ratio / responsive sizing. |
v-sheet |
A themed surface — the base for cards and panels. |
v-divider |
Horizontal or vertical separating line. |
Buttons & Actions
| Component | Purpose |
|---|---|
v-btn |
Button (text, outlined, flat, icon, block, loading…). |
v-btn-group / v-btn-toggle |
Grouped and toggle-selectable buttons. |
v-icon-btn |
Icon-only button variant. |
v-fab |
Floating action button. |
v-speed-dial |
FAB that expands to reveal more actions. |
v-icon |
Render an icon from any configured set. |
Form Inputs & Controls
| Component | Purpose |
|---|---|
v-text-field |
Single-line text input. |
v-textarea |
Multi-line text input. |
v-select |
Dropdown selection. |
v-autocomplete |
Select with type-ahead filtering. |
v-combobox |
Autocomplete that also accepts free input. |
v-checkbox / v-radio / v-radio-group |
Selection controls. |
v-switch |
On/off toggle. |
v-slider / v-range-slider |
Single and dual-handle sliders. |
v-file-input / v-file-upload |
File selection and upload. |
v-otp-input |
One-time-password entry. |
v-number-input |
Numeric input with steppers. |
v-color-input / v-color-picker |
Color selection. |
v-date-input / v-date-picker |
Date selection. |
v-time-picker |
Time selection. |
v-rating |
Star/icon rating input. |
v-form |
Form wrapper with validation orchestration. |
v-input / v-field / v-label / v-messages / v-counter |
Building blocks shared by the inputs above. |
v-selection-control / v-selection-control-group |
Low-level primitives for checkbox/radio/switch. |
v-confirm-edit |
Inline edit-with-confirm wrapper. |
v-validation |
Standalone validation wrapper. |
Data Presentation
| Component | Purpose |
|---|---|
v-data-table |
Sortable, paginated, selectable data table (with server-side and virtual variants). |
v-data-iterator |
Headless iteration over data with the same sorting/paging logic. |
v-table |
Plain styled HTML table. |
v-list |
Vertical list with items, groups, subheaders, dividers. |
v-virtual-scroll |
Renders only visible rows for huge lists. |
v-treeview |
Hierarchical tree with selection (labs/stable). |
v-timeline |
Vertical/horizontal event timeline. |
v-sparkline |
Inline mini line/bar chart. |
v-infinite-scroll |
Load more content as the user scrolls. |
v-pagination |
Page navigation control. |
Containers & Surfaces
| Component | Purpose |
|---|---|
v-card |
Material card with title, text, actions. |
v-expansion-panel |
Accordion-style collapsible panels. |
v-banner |
Prominent message banner. |
v-alert |
Contextual alert/notification box. |
v-chip / v-chip-group |
Compact tags / filter chips. |
v-avatar |
Circular image/initials container. |
v-badge |
Small status indicator overlaid on a child. |
v-empty-state |
Friendly placeholder for empty content. |
v-kbd / v-code |
Keyboard-key and inline-code styling. |
Navigation & Disclosure
| Component | Purpose |
|---|---|
v-tabs |
Tabbed navigation. |
v-breadcrumbs |
Breadcrumb trail. |
v-bottom-navigation |
Mobile bottom navigation bar. |
v-stepper / v-stepper-vertical |
Multi-step wizard. |
v-window |
Swipeable/animated view container. |
v-carousel |
Image/content carousel. |
v-menu |
Anchored popup menu. |
v-dialog |
Modal dialog. |
v-bottom-sheet |
Sheet that slides up from the bottom. |
v-overlay |
Low-level scrim/positioning primitive behind menus and dialogs. |
v-tooltip |
Hover/focus tooltip. |
v-snackbar / v-snackbar-queue |
Transient toast notifications (single and queued). |
Feedback & Progress
| Component | Purpose |
|---|---|
v-progress-linear |
Linear progress / loading bar. |
v-progress-circular |
Circular progress spinner. |
v-skeleton-loader |
Placeholder shimmer while content loads. |
v-pull-to-refresh |
Pull-down-to-refresh gesture wrapper. |
Media & Utility
| Component | Purpose |
|---|---|
v-img |
Responsive image with lazy-loading and placeholders. |
v-parallax |
Parallax-scrolling image. |
v-hover |
Exposes hover state to its slot. |
v-lazy |
Defers rendering until the element scrolls into view. |
v-no-ssr |
Renders children only on the client. |
v-hotkey |
Displays and binds keyboard shortcuts. |
v-item-group |
Generic selectable group logic. |
v-slide-group |
Horizontally scrollable group of items. |
Providers (configuration components)
| Component | Purpose |
|---|---|
v-theme-provider |
Apply a theme to a subtree. |
v-locale-provider |
Apply a locale / RTL to a subtree. |
v-defaults-provider |
Apply prop defaults to a subtree. |
Labs (experimental)
The labs entry contains newer components whose API may still change: VAvatarGroup,
VCommandPalette, VDateRangePicker, VHeatmap, VHighlight, VMaskInput,
VMonthPicker, VPie, VProgress, and VVideo. Opt in explicitly:
import { createVuetify } from 'vuetify'
import { VTreeview } from 'vuetify/labs/VTreeview'
createVuetify({
components: { VTreeview },
})
Warning
Labs components are production-usable but not API-stable. Pin your Vuetify version and review the changelog before upgrading if you depend on a labs component.
Example: a small but real form
<template>
<v-card class="mx-auto pa-6" max-width="420">
<v-card-title>Sign up</v-card-title>
<v-form @submit.prevent="onSubmit">
<v-text-field
v-model="email"
label="Email"
:rules="[v => !!v || 'Required', v => /.+@.+/.test(v) || 'Invalid email']"
prepend-inner-icon="$info"
/>
<v-text-field v-model="password" label="Password" type="password" />
<v-btn type="submit" color="primary" block class="mt-2">Create account</v-btn>
</v-form>
</v-card>
</template>
<script setup>
import { ref } from 'vue'
const email = ref('')
const password = ref('')
function onSubmit (e) { /* e is a SubmitEventPromise — see Forms section */ }
</script>
13. Forms & Validation
<v-form> coordinates validation across the inputs it contains. Each input registers
itself with the form; the form aggregates their validity into a single reactive state.
Per-field rules
Every input accepts a :rules array. Each rule is a function returning true when valid or
a string error message when invalid:
<v-text-field
v-model="username"
label="Username"
:rules="[
v => !!v || 'Username is required',
v => v.length >= 3 || 'Must be at least 3 characters',
]"
/>
Form-level validation
<script setup>
import { ref } from 'vue'
const form = ref(null)
async function submit () {
const { valid } = await form.value.validate()
if (valid) {
// all fields passed — proceed
}
}
function clear () {
form.value.reset() // clear values and validation
form.value.resetValidation() // clear only validation messages
}
</script>
<template>
<v-form ref="form">
<!-- inputs -->
<v-btn @click="submit">Submit</v-btn>
<v-btn @click="clear">Reset</v-btn>
</v-form>
</template>
When validation runs
The validate-on prop controls timing. The default is input (validate as the user
types). Other values let you validate on blur, on submit, or combinations such as
blur lazy.
<v-form validate-on="submit">…</v-form>
Submitting
<v-form>'s @submit event provides a SubmitEventPromise — a native submit event
that is also a promise resolving to { valid, errors }:
<v-form @submit.prevent="(e) => e.then(({ valid }) => valid && save())">
| Prop / option | Effect |
|---|---|
validate-on |
When fields validate (input default, blur, submit, lazy…). |
fast-fail |
Stop validating at the first failing field. |
disabled |
Disable every input in the form. |
readonly |
Make every input read-only. |
Tip
Use
fast-failon large forms where you only need to know whether the form is valid, not collect every error at once — it stops at the first failure.
14. Directives
Directives add low-level DOM behavior to any element, not just Vuetify components. Register
them globally (createVuetify({ directives })) or individually. Eight directives ship:
| Directive | Usage | Purpose |
|---|---|---|
v-ripple |
v-ripple |
Material "ink" ripple on click/touch. |
v-resize |
v-resize="onResize" |
Call a handler when the window resizes. |
v-intersect |
v-intersect="onIntersect" |
Fire when the element enters/leaves the viewport (IntersectionObserver). |
v-mutate |
v-mutate="onMutate" |
Fire when the element's DOM changes (MutationObserver). |
v-scroll |
v-scroll="onScroll" |
Call a handler on scroll. |
v-touch |
v-touch="{ left, right, up, down }" |
Detect swipe gestures. |
v-click-outside |
v-click-outside="onOutside" |
Fire when a click lands outside the element. |
v-tooltip |
v-tooltip="'Help text'" |
Attach a tooltip directly to an element. |
<div v-ripple class="pa-4">Click me for a ripple</div>
<div v-intersect="(isIntersecting) => visible = isIntersecting">
Lazy-loaded section
</div>
<v-card v-touch="{ left: next, right: prev }">Swipe me</v-card>
Note
Many components use these directives internally —
<v-btn>, for example, appliesv-rippleautomatically (toggle it with therippleprop). You only need to register a directive yourself when applying it to your own elements.
15. Programmatic Scrolling
The useGoTo() composable performs smooth, animated scrolling to a target — an element,
a CSS selector, a component ref, or a pixel offset.
<script setup>
import { useGoTo } from 'vuetify'
const goTo = useGoTo()
function scrollToFaq () {
goTo('#faq') // to an element by selector
}
function scrollToTop () {
goTo(0) // to a pixel offset
}
function scrollSidewaysTo () {
goTo.horizontal('#col-5') // horizontal scrolling
}
</script>
Configure duration, easing, offset, and container globally or per call:
createVuetify({
goTo: {
duration: 300, // ms (default)
easing: 'easeInOutCubic', // default easing pattern
offset: 0,
},
})
goTo('#section', { duration: 600, offset: -64 }) // offset accounts for a fixed app bar
Note
useGoToautomatically falls back to an instant jump when the user prefers reduced motion, and it respects RTL for horizontal scrolling.
16. Styling, Utility Classes & SASS
Utility classes
Vuetify generates utility classes for theme colors and common layout needs:
- Color:
bg-primary,text-secondary,border-error(one set per theme color). - Spacing:
pa-4(padding all),ma-2(margin all),mt-3,px-6, etc., on a 4px scale. - Display & elevation: responsive display helpers and
elevation-0…elevation-24. - Screen-reader and pointer-event helpers.
<v-sheet class="bg-primary text-white pa-6 rounded-lg elevation-4">
Themed, padded, elevated surface
</v-sheet>
Importing precompiled CSS
The simplest option imports the precompiled stylesheet:
import 'vuetify/styles'
Customizing with SASS variables
To override Vuetify's SASS variables (colors, spacing scale, component variables), configure the Vite or Webpack plugin to point at your own SASS entry, then import the settings:
// src/styles/settings.scss
@use 'vuetify/settings' with (
$button-height: 44px,
$border-radius-root: 6px,
);
The framework exposes three SASS entry points:
| Entry | Contents |
|---|---|
vuetify |
Global CSS (reset, utilities), no component styles. |
vuetify/settings |
All SASS variables, including per-component variables. |
vuetify/tools |
Mixins and functions. |
Tip
Editing SASS variables requires the build plugin (
vite-plugin-vuetifyorwebpack-plugin-vuetify) with itsstyles.configFileoption set — the precompiledvuetify/stylesimport cannot be customized after the fact.
17. Tree-Shaking & Production Builds
Importing every component is fine for prototypes but bloats production bundles. The official build plugins import only the components you actually use, automatically.
Vite plugin
import vuetify from 'vite-plugin-vuetify'
export default defineConfig({
plugins: [
vuetify({ autoImport: true }), // auto-imports only used components + directives
],
})
With autoImport: true you simply write <v-btn> in a template and the plugin adds the
matching import behind the scenes — no manual import and no full-library bundle.
Manual per-component imports
Without the plugin, import components from their grouped path:
import { VBtn } from 'vuetify/components/VBtn'
import { VList, VListItem } from 'vuetify/components/VList'
Note
Components are grouped by their top-level name.
VListItem,VListGroup, andVListItemTitleall live undervuetify/components/VList. Directives import fromvuetify/directives/<name>.
18. Server-Side Rendering (SSR)
Vue 3 cannot auto-detect SSR, so frameworks like Nuxt and Gridsome must tell Vuetify
explicitly by setting ssr: true:
import '@mdi/font/css/materialdesignicons.css'
import 'vuetify/styles'
import { createVuetify } from 'vuetify'
const vuetify = createVuetify({
ssr: true,
})
You may pass an object to seed the display dimensions used during the server render:
createVuetify({
ssr: { clientWidth: 1280, clientHeight: 800 },
})
Important
When
ssris enabled, Vuetify defers its first display update until after hydration to avoid a hydration mismatch between the server-rendered and client-rendered markup. If you see hydration warnings, confirmssr: trueis set.
19. Troubleshooting
The issues below are drawn from the framework's own issue tracker and the behaviors visible in source. Each lists the symptom, the cause, and what to do.
Error: Could not find Vuetify theme injection
- Cause:
useTheme()(or anotheruse*composable) was called outside a component'ssetup(), or the component is not a descendant of an app that registered thecreateVuetifyplugin. - Solution: Call the composable inside
<script setup>/setup(), and ensureapp.use(vuetify)ran and your UI is wrapped in<v-app>. - Prevention: Never call
useTheme/useDisplay/useLocalefrom plain module scope.
Error: Could not find aliased icon "$xyz"
- Cause: An icon value used a
$aliasthat is not defined in youricons.aliases. - Solution: Add the alias to your
icons.aliases, or import the set's defaultaliasesand spread them:aliases: { ...aliases, xyz: mdiSomething }. - Prevention: Always pass the set's
aliasesexport tocreateVuetify.
Theme toggle() behaves inconsistently after enabling theme transitions
- Cause: With animated transitions enabled, a transition origin set by a previous call
can carry over (tracked upstream for
ThemeInstance.toggle()). - Solution: Call
theme.setTransitionOrigin(event)immediately before the toggle, or toggle without a transition (theme.toggle(undefined, false)) when you do not need the animation.
VBtn size as a number does not change the font size
- Cause: Numeric
sizecontrols dimensions but not typography in the current version. - Solution: Use a named size (
x-small,small,default,large,x-large) or add a text utility class (text-h6) for font scaling.
VSelect with multiple and duplicate items cannot deselect an option
- Cause: Duplicate item values make selection ambiguous.
- Solution: Ensure each item has a unique value via
item-value, or de-duplicate the items array before binding it.
VDialog with a percentage max-width is not respected
- Cause: Percentage widths on the dialog were affected by a layout change.
- Solution: Use an explicit pixel/
remmax-width, or wrap the dialog content in a sized container.
MDI icons do not render in an offline PWA
- Cause: The Material Design Icons font files were not cached by the service worker, so glyphs cannot load offline.
- Solution: Ensure your PWA precaches the
@mdi/fontfiles, or switch to the tree-shakeable SVG set (vuetify/iconsets/mdi-svg) so icon paths are bundled into JS.
Icons / styles don't appear at all
- Cause: Missing stylesheet or font import.
- Solution: Confirm you imported
'vuetify/styles'and an icon font/CSS (for example'@mdi/font/css/materialdesignicons.css'), and that the app is inside<v-app>.
Tip
For a question that isn't covered here, the Vuetify community Discord and the issue generator at
issues.vuetifyjs.comare the official support channels.
20. Frequently Asked Questions
Q: Which Vue version does Vuetify 4 require? Vue 3.5.0 or later. Vuetify 4 does not support Vue 2.
Q: Do I have to import each component?
No. With create-vuetify or the Vite plugin's autoImport, components are registered and
tree-shaken automatically. Manual imports are only needed if you are not using the plugin.
Q: Why do I need <v-app>?
It establishes the layout root and hosts theming and overlay providers. Components like
menus, dialogs, app bars, and drawers rely on it to position correctly.
Q: How do I switch between light and dark mode?
Call useTheme().toggle(), or set theme.defaultTheme to 'system' to follow the OS.
Q: Does Vuetify follow the operating system's dark mode automatically?
Yes. The default theme is 'system', which tracks prefers-color-scheme and updates live.
Q: How do I change the default look of all buttons (or any component)?
Use the defaults option in createVuetify, keyed by the component name (e.g. VBtn), or
wrap a subtree in <v-defaults-provider>.
Q: Can I use a font icon library other than Material Design Icons?
Yes. Choose from 15 built-in sets (Font Awesome, Lucide, Tabler, Phosphor, Remix, Carbon,
BoxIcons, Material Symbols, and more) via icons.defaultSet, or mix sets using the
set:name prefix.
Q: How do I make my app responsive?
Use useDisplay() for reactive breakpoint booleans (mobile, mdAndUp, etc.) and the
12-column grid (<v-row> / <v-col>) with responsive props.
Q: Which languages are supported out of the box? 43, with English as the fallback. You can also wire Vuetify into an existing vue-i18n setup.
Q: How do I support right-to-left languages?
Arabic, Persian, and Hebrew are RTL by default. Use <v-locale-provider rtl> or
useRtl() to control direction.
Q: My date picker needs a specific date library — is that possible?
Yes. The built-in adapter uses native Intl; for date-fns, luxon, dayjs, or
moment, pass the matching @date-io adapter to the date option.
Q: Are the "labs" components safe to use? They work, but their API is not stable. Pin your Vuetify version and check the changelog before upgrading.
Q: How do I customize SASS variables?
Configure the Vite/Webpack plugin's styles.configFile and @use 'vuetify/settings' with
your overrides. The precompiled vuetify/styles import cannot be customized.
Q: How do I reduce my production bundle size?
Use the Vite plugin with autoImport: true, the SVG icon set, and import only the locales
you ship.
21. Reference
createVuetify options at a glance
| Option | Purpose |
|---|---|
theme |
Light/dark/custom palettes, default theme, transitions, variations. |
display |
Breakpoint thresholds and the mobile breakpoint. |
icons |
Default icon set, sets, and $ aliases. |
locale |
Translations, fallback, RTL, separators, i18n adapter. |
date |
Date adapter and locale map for date components. |
defaults |
Global and per-component default prop values. |
goTo |
Default scroll duration, easing, offset, container. |
blueprint |
A preset bundle (md1/md2/md3) merged under your options. |
components / directives / aliases |
Explicit registration of components, directives, and component aliases. |
ssr |
Enable server-side rendering support. |
Breakpoint thresholds (px)
xs |
sm |
md |
lg |
xl |
xxl |
|---|---|---|---|---|---|
| 0 | 600 | 840 | 1145 | 1545 | 2138 |
Default theme colors
| Token | Light | Dark |
|---|---|---|
primary |
#1867C0 |
#2196F3 |
secondary |
#48A9A6 |
#54B6B2 |
background |
#FFFFFF |
#121212 |
surface |
#FFFFFF |
#212121 |
success |
#4CAF50 |
#4CAF50 |
info |
#2196F3 |
#2196F3 |
warning |
#FB8C00 |
#FB8C00 |
error |
#B00020 |
#CF6679 |
Composables
| Composable | Returns |
|---|---|
useTheme() |
Theme instance: name, current, change, toggle, cycle. |
useDisplay() |
Breakpoint booleans, mobile, width/height, platform. |
useLocale() |
Current locale, translation function. |
useRtl() |
isRtl, rtlClasses. |
useGoTo() |
Smooth-scroll function (with .horizontal). |
useDefaults() |
Resolved props merged with global/scoped defaults. |
Key exports
| Path | Contents |
|---|---|
vuetify |
createVuetify, public composables. |
vuetify/styles |
Precompiled global CSS. |
vuetify/components / vuetify/components/<Name> |
All / grouped components. |
vuetify/directives / vuetify/directives/<name> |
All / individual directives. |
vuetify/blueprints/<name> |
md1, md2, md3 blueprints. |
vuetify/locale / vuetify/locale/adapters/<name> |
Translations / i18n adapters. |
vuetify/iconsets/<name> |
Icon set presets. |
Fonts via unplugin-fonts (recommended config)
import { defineConfig } from 'vite'
import ViteFonts from 'unplugin-fonts/vite'
export default defineConfig({
plugins: [
ViteFonts({
fontsource: {
families: [
{ name: 'Roboto', weights: [100, 300, 400, 500, 700, 900], styles: ['normal', 'italic'] },
],
},
}),
],
})
// main.ts
import 'unfonts.css'