
Material Design 3
Google's latest design system with modern aesthetics and accessibility
80+ Components
Everything from buttons to complex data tables and calendars
Enterprise Ready
Long-term support, professional themes, and dedicated support options
Add Vuetify to your Nuxt project using the official module:
npm install vuetify-nuxt-module vuetify
Configure in nuxt.config.ts:
export default defineNuxtConfig({
modules: ["vuetify-nuxt-module"],
vuetify: {
moduleOptions: {
styles: true,
autoImport: true,
},
},
})
Create a new project with Vuetify:
npm create vuetify@latest
Or add to an existing Vue project:
npm install vuetify
// main.ts
import { createApp } from "vue"
import { createVuetify } from "vuetify"
import "vuetify/styles"
import App from "./App.vue"
const vuetify = createVuetify()
const app = createApp(App)
app.use(vuetify)
app.mount("#app")
Vuetify buttons with variants, colors, and icons:
<template>
<div class="d-flex ga-2">
<v-btn>Default</v-btn>
<v-btn color="primary">Primary</v-btn>
<v-btn color="success">Success</v-btn>
<v-btn color="error" variant="outlined">Error</v-btn>
<v-btn color="warning" variant="tonal">Warning</v-btn>
<v-btn icon="mdi-heart" color="pink" />
</div>
</template>
Form inputs with built-in validation:
<script setup>
const email = ref("")
const rules = [(v) => !!v || "Email is required", (v) => /.+@.+\..+/.test(v) || "Invalid email"]
</script>
<template>
<v-text-field
v-model="email"
label="Email"
placeholder="Enter your email"
prepend-inner-icon="mdi-email"
:rules="rules"
/>
</template>
Create beautiful card layouts:
<template>
<v-card max-width="400">
<v-img src="/images/hero.jpg" height="200" cover />
<v-card-title>Card Title</v-card-title>
<v-card-subtitle>Subtitle text</v-card-subtitle>
<v-card-text> This is the card content. You can add any text or components here. </v-card-text>
<v-card-actions>
<v-btn color="primary">Action</v-btn>
<v-btn variant="text">Cancel</v-btn>
</v-card-actions>
</v-card>
</template>
Vuetify uses a 12-point flexbox grid system:
<template>
<v-container>
<v-row>
<v-col cols="12" md="6" lg="4">
<v-card>Column 1</v-card>
</v-col>
<v-col cols="12" md="6" lg="4">
<v-card>Column 2</v-card>
</v-col>
<v-col cols="12" md="12" lg="4">
<v-card>Column 3</v-card>
</v-col>
</v-row>
</v-container>
</template>
xs - Extra small (< 600px)sm - Small (600px - 960px)md - Medium (960px - 1280px)lg - Large (1280px - 1920px)xl - Extra large (1920px - 2560px)xxl - Extra extra large (> 2560px)Build forms with validation:
<script setup>
const form = ref(null)
const valid = ref(false)
const state = reactive({
name: "",
email: "",
password: "",
})
const nameRules = [
(v) => !!v || "Name is required",
(v) => v.length >= 2 || "Name must be at least 2 characters",
]
const emailRules = [
(v) => !!v || "Email is required",
(v) => /.+@.+\..+/.test(v) || "Invalid email",
]
const passwordRules = [
(v) => !!v || "Password is required",
(v) => v.length >= 8 || "Password must be at least 8 characters",
]
async function submit() {
const { valid } = await form.value.validate()
if (valid) {
console.log("Form submitted:", state)
}
}
</script>
<template>
<v-form ref="form" v-model="valid" @submit.prevent="submit">
<v-text-field v-model="state.name" label="Name" :rules="nameRules" />
<v-text-field v-model="state.email" label="Email" type="email" :rules="emailRules" />
<v-text-field
v-model="state.password"
label="Password"
type="password"
:rules="passwordRules"
/>
<v-btn type="submit" color="primary" :disabled="!valid" block> Submit </v-btn>
</v-form>
</template>
Vuetify uses Material Design Icons by default:
<template>
<div class="d-flex ga-4 align-center">
<v-icon icon="mdi-home" />
<v-icon icon="mdi-account" size="large" />
<v-icon icon="mdi-heart" color="red" />
<v-icon icon="mdi-star" color="amber" size="x-large" />
</div>
</template>
Use icons in components:
<template>
<v-btn prepend-icon="mdi-plus">Add Item</v-btn>
<v-btn icon="mdi-delete" color="error" />
<v-text-field prepend-icon="mdi-magnify" label="Search" />
<v-chip prepend-icon="mdi-check" color="success">Completed</v-chip>
</template>
Browse all icons at Material Design Icons.
Customize your app's look with themes:
// plugins/vuetify.ts
import { createVuetify } from "vuetify"
export default createVuetify({
theme: {
defaultTheme: "light",
themes: {
light: {
colors: {
primary: "#1976D2",
secondary: "#424242",
accent: "#82B1FF",
error: "#FF5252",
info: "#2196F3",
success: "#4CAF50",
warning: "#FB8C00",
},
},
dark: {
colors: {
primary: "#2196F3",
secondary: "#424242",
},
},
},
},
})
Access theme colors in your components:
<template>
<v-card color="primary">
<v-card-text class="text-white">Primary colored card</v-card-text>
</v-card>
<v-btn color="secondary">Secondary Button</v-btn>
<v-alert color="success">Success message</v-alert>
</template>
Toggle between light and dark themes:
<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
:icon="theme.global.current.value.dark ? 'mdi-weather-sunny' : 'mdi-weather-night'"
@click="toggleTheme"
/>
</template>
Create a navigation header:
<template>
<v-app-bar color="primary">
<v-app-bar-nav-icon @click="drawer = !drawer" />
<v-app-bar-title>My App</v-app-bar-title>
<v-spacer />
<v-btn icon="mdi-magnify" />
<v-btn icon="mdi-dots-vertical" />
</v-app-bar>
</template>
Build a sidebar navigation:
<script setup>
const drawer = ref(true)
const items = [
{ title: "Home", icon: "mdi-home", to: "/" },
{ title: "Dashboard", icon: "mdi-view-dashboard", to: "/dashboard" },
{ title: "Settings", icon: "mdi-cog", to: "/settings" },
{ title: "About", icon: "mdi-information", to: "/about" },
]
</script>
<template>
<v-navigation-drawer v-model="drawer">
<v-list nav>
<v-list-item
v-for="item in items"
:key="item.title"
:prepend-icon="item.icon"
:title="item.title"
:to="item.to"
/>
</v-list>
</v-navigation-drawer>
</template>
Organize content with tabs:
<script setup>
const tab = ref("one")
</script>
<template>
<v-tabs v-model="tab" color="primary">
<v-tab value="one">Tab One</v-tab>
<v-tab value="two">Tab Two</v-tab>
<v-tab value="three">Tab Three</v-tab>
</v-tabs>
<v-tabs-window v-model="tab">
<v-tabs-window-item value="one">Content for tab one</v-tabs-window-item>
<v-tabs-window-item value="two">Content for tab two</v-tabs-window-item>
<v-tabs-window-item value="three">Content for tab three</v-tabs-window-item>
</v-tabs-window>
</template>
<script setup>
const dialog = ref(false)
</script>
<template>
<v-btn @click="dialog = true">Open Dialog</v-btn>
<v-dialog v-model="dialog" max-width="500">
<v-card>
<v-card-title>Dialog Title</v-card-title>
<v-card-text> This is the dialog content. Add your message or form here. </v-card-text>
<v-card-actions>
<v-spacer />
<v-btn @click="dialog = false">Cancel</v-btn>
<v-btn color="primary" @click="dialog = false">Confirm</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup>
const snackbar = ref(false)
const message = ref("")
function showSnackbar(msg) {
message.value = msg
snackbar.value = true
}
</script>
<template>
<v-btn @click="showSnackbar('Changes saved successfully!')">Save</v-btn>
<v-snackbar v-model="snackbar" :timeout="3000" color="success">
{{ message }}
<template #actions>
<v-btn variant="text" @click="snackbar = false">Close</v-btn>
</template>
</v-snackbar>
</template>
Display and manage data:
<script setup>
const headers = [
{ title: "Name", key: "name" },
{ title: "Email", key: "email" },
{ title: "Role", key: "role" },
{ title: "Actions", key: "actions", sortable: false },
]
const users = [
{ name: "Alice Johnson", email: "alice@example.com", role: "Admin" },
{ name: "Bob Smith", email: "bob@example.com", role: "User" },
{ name: "Carol White", email: "carol@example.com", role: "Editor" },
]
</script>
<template>
<v-data-table :headers="headers" :items="users" class="elevation-1">
<template #item.actions="{ item }">
<v-btn icon="mdi-pencil" size="small" variant="text" />
<v-btn icon="mdi-delete" size="small" variant="text" color="error" />
</template>
</v-data-table>
</template>
Create interactive lists:
<script setup>
const items = [
{ title: "Photos", subtitle: "Jan 9, 2025", icon: "mdi-folder-image" },
{ title: "Documents", subtitle: "Jan 7, 2025", icon: "mdi-folder-text" },
{ title: "Downloads", subtitle: "Jan 5, 2025", icon: "mdi-folder-download" },
]
</script>
<template>
<v-list>
<v-list-item
v-for="item in items"
:key="item.title"
:prepend-icon="item.icon"
:title="item.title"
:subtitle="item.subtitle"
>
<template #append>
<v-icon icon="mdi-chevron-right" />
</template>
</v-list-item>
</v-list>
</template>
Display tags and selections:
<template>
<div class="d-flex ga-2 flex-wrap">
<v-chip>Default</v-chip>
<v-chip color="primary">Primary</v-chip>
<v-chip color="success" prepend-icon="mdi-check">Completed</v-chip>
<v-chip color="error" closable>Removable</v-chip>
<v-chip color="info" variant="outlined">Outlined</v-chip>
</div>
</template>
<template>
<div class="d-flex flex-column ga-4">
<!-- Circular -->
<v-progress-circular indeterminate color="primary" />
<!-- Linear -->
<v-progress-linear indeterminate color="primary" />
<!-- Determinate -->
<v-progress-linear :model-value="75" color="success" />
</div>
</template>
<template>
<v-skeleton-loader type="card" />
<v-skeleton-loader type="article" />
<v-skeleton-loader type="list-item-avatar-three-line" />
</template>
Material Design 3
Built on Google's latest design language with modern components
TypeScript Support
Full type definitions for better developer experience
Accessibility
WCAG compliant with keyboard navigation and ARIA support