Guide·

Getting Started with Vuetify

Build beautiful Material Design applications with Vuetify, the complete Vue UI framework.
Getting Started with Vuetify
Vuetify is a comprehensive Vue UI library built on Material Design 3. It provides 80+ handcrafted components, a powerful grid system, and extensive customization options for building polished applications.

Why Vuetify?

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

Installation

With Nuxt

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,
    },
  },
})

With Vue + Vite

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")

Basic Components

Buttons

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>

Text Fields

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>

Cards

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>

Grid System

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>

Responsive Breakpoints

  • 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)

Form Handling

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>

Icons

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.

Theming

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",
        },
      },
    },
  },
})

Custom Theme Colors

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>

Dark Mode

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>

App Bar

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>

Tabs

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>

Dialogs and Overlays

Dialog

<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>

Snackbar Notifications

<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>

Data Display

Data Tables

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>

Lists

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>

Chips

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>

Loading States

Progress Indicators

<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>

Skeleton Loaders

<template>
  <v-skeleton-loader type="card" />
  <v-skeleton-loader type="article" />
  <v-skeleton-loader type="list-item-avatar-three-line" />
</template>

Key Features

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

Explore the full Vuetify documentation for all components, directives, and composables.
Enjoyed this post?
Subscribe to get notified when I publish new articles.

Need a Full Stack Engineer?

10+ years building performant web applications. Let's talk about your next project.