Skip to content

UI Component Framework

Why Vuetify?

There are number of UI frameworks for Vue out there. Check this comparison. Vuetify is often considered one of the best UI component frameworks for Vue.js due to Material design, Rich Component Library and by design Responsive Design.

Add Vuetify to the project

  1. Install Vuetify Component Framework
ps
npm install vuetify
npm install -D vite-plugin-vuetify
  1. Add Vuetify Plugin to Vite configuration /vite.config.ts to enable Hot Module Reloading for Vuetify components.
ts
// ...
import Vuetify from 'vite-plugin-vuetify'

export default defineConfig({
  plugins: [
    // ...
    Vuetify(),
  ],
  // ...
})
  1. Create new file @/plugins/vuetify.ts with Vuetify configuration.
ts
import 'vuetify/styles'
import { createVuetify } from 'vuetify'

export default createVuetify({})
  1. Modify @/main.ts to use Vuetify
ts
// ...
import vuetify from './plugins/vuetify'

const app = createApp(App)

// ...
app.use(vuetify)

app.mount('#app')
  1. Create new test page @/pages/sandbox/index.vue and open http://localhost:5173/sandbox to see Vuetify UI Components in action.
vue
<template>
  <v-card>
    <v-card-title>Title</v-card-title>
    <v-card-text>Content</v-card-text>
    <v-card-actions>
      <v-btn color="primary">Primary</v-btn>
      <v-btn color="secondary">Secondary</v-btn>
    </v-card-actions>
  </v-card>
</template>

Blueprints

Blueprints is a collection of pre-defined Material design defaults. Choose the blueprint and add in @/plugins/vuetify.ts

ts
// ...
import { md3 } from 'vuetify/blueprints'
// ...
export default createVuetify({
  // ...
  blueprint: md3,
})

Themes

Theme allows to create different themes for your application.

  1. Create light and dark themes.
@/themes/light.ts
ts
export default {
  dark: false,
  colors: {
    primary: '#9155FD',
    secondary: '#8A8D93',
    'on-secondary': '#fff',
    success: '#56CA00',
    info: '#16B1FF',
    warning: '#FFB400',
    error: '#FF4C51',
    'on-primary': '#FFFFFF',
    'on-success': '#FFFFFF',
    'on-warning': '#FFFFFF',
    background: '#F4F5FA',
    'on-background': '#3A3541',
    'on-surface': '#3A3541',
    'grey-50': '#FAFAFA',
    'grey-100': '#F0F2F8',
    'grey-200': '#EEEEEE',
    'grey-300': '#E0E0E0',
    'grey-400': '#BDBDBD',
    'grey-500': '#9E9E9E',
    'grey-600': '#757575',
    'grey-700': '#616161',
    'grey-800': '#424242',
    'grey-900': '#212121',
    'perfect-scrollbar-thumb': '#DBDADE',
    'skin-bordered-background': '#fff',
    'skin-bordered-surface': '#fff',
  },
  variables: {
    'code-color': '#d400ff',
    'overlay-scrim-background': '#3A3541',
    'overlay-scrim-opacity': 0.5,
    'hover-opacity': 0.04,
    'focus-opacity': 0.1,
    'selected-opacity': 0.12,
    'activated-opacity': 0.1,
    'pressed-opacity': 0.14,
    'dragged-opacity': 0.1,
    'border-color': '#3A3541',
    'table-header-background': '#F9FAFC',
    'custom-background': '#F9F8F9',
    'shadow-key-umbra-opacity': 'rgba(var(--v-theme-on-surface), 0.08)',
    'shadow-key-penumbra-opacity': 'rgba(var(--v-theme-on-surface), 0.12)',
    'shadow-key-ambient-opacity': 'rgba(var(--v-theme-on-surface), 0.04)',
  },
}
@/themes/dark.ts
ts
export default {
  dark: true,
  colors: {
    primary: '#9155FD',
    secondary: '#8A8D93',
    'on-secondary': '#fff',
    success: '#56CA00',
    info: '#16B1FF',
    warning: '#FFB400',
    error: '#FF4C51',
    'on-primary': '#FFFFFF',
    'on-success': '#FFFFFF',
    'on-warning': '#FFFFFF',
    background: '#28243D',
    'on-background': '#E7E3FC',
    surface: '#111111',
    'on-surface': '#E7E3FC',
    'grey-50': '#2A2E42',
    'grey-100': '#474360',
    'grey-200': '#4A5072',
    'grey-300': '#5E6692',
    'grey-400': '#7983BB',
    'grey-500': '#8692D0',
    'grey-600': '#AAB3DE',
    'grey-700': '#B6BEE3',
    'grey-800': '#CFD3EC',
    'grey-900': '#E7E9F6',
    'perfect-scrollbar-thumb': '#4A5072',
    'skin-bordered-background': '#111111',
    'skin-bordered-surface': '#111111',
  },
  variables: {
    'code-color': '#d400ff',
    'overlay-scrim-background': '#2C2942',
    'overlay-scrim-opacity': 0.6,
    'hover-opacity': 0.04,
    'focus-opacity': 0.1,
    'selected-opacity': 0.12,
    'activated-opacity': 0.1,
    'pressed-opacity': 0.14,
    'dragged-opacity': 0.1,
    'border-color': '#E7E3FC',
    'table-header-background': '#3D3759',
    'custom-background': '#373452',
    'shadow-key-umbra-opacity': 'rgba(20, 18, 33, 0.08)',
    'shadow-key-penumbra-opacity': 'rgba(20, 18, 33, 0.12)',
    'shadow-key-ambient-opacity': 'rgba(20, 18, 33, 0.04)',
  },
}
  1. Import and add customized color schemes for light and dark themes in @/plugins/vuetify.ts.
ts
// ...
import light from '@/themes/light'
import dark from '@/themes/dark'
// ...
export default createVuetify({
// ...
  theme: {
    defaultTheme: 'light',
    themes: {
      light,
      dark,
    },
  },
//...
})
  1. Add theme toggling to @\pages\sandbox\index.vue and check how it works.
vue
<template>
  <v-card>
    <v-card-title>Title</v-card-title>
    <v-card-text>Content</v-card-text>
    <v-card-actions>
      <v-btn color="primary">Primary</v-btn>
      <v-btn color="secondary">Secondary</v-btn>
      <v-spacer></v-spacer>
      <v-btn @click="toggleTheme">Toggle theme</v-btn>
    </v-card-actions>
  </v-card>
</template>

<script setup lang="ts">
import { useTheme } from 'vuetify'

const theme = useTheme()

function toggleTheme() {
  theme.global.name.value = theme.global.current.value.dark ? 'light' : 'dark'
}
</script>

Global configuration

Global configuration is one of the most powerful features allowing you to set default prop values globally or per component when setting up your application. As an example, to make all card action buttons outlined globally:

  1. Create file @/themes/defaults.ts
ts
export default {
  VCardActions: {
    VBtn: { variant: 'outlined' },
    class: 'd-flex flex-wrap',
  },
}
  1. Import defaults in @/plugins/vuetify.ts
ts
// ...
import defaults from '@/themes/defaults'
// ...
export default createVuetify({
  // ...
  defaults,
  // ...
})

Icons

To keep bundled size low, it is important to include only icons that are used in the project.

  1. Install icon library
ps
npm install @mdi/js
  1. Create file of used icons @/themes/icons.ts
ts
import { mdiHome } from '@mdi/js'
export default { mdiHome }
  1. Imports icons into @/plugins/vuetify.ts
ts
// ...
import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'
import icons from '@/themes/icons'
// ...
export default createVuetify({
  //...
  icons: {
    defaultSet: 'mdi',
    aliases: {
      ...aliases,
      ...icons,
    },
    sets: {
      mdi,
    },
  },
  //...
})
  1. Add icon to @/pages/sandbox.vue
vue
...
<v-card-title><v-icon icon="$mdiHome" />Title</v-card-title>
...

Cherries

It is always nice to add some small nuance that differentiates your app from others. Here is an example to provide customized background style.

  1. Create a new composable @\composables\ui.ts for enhanced UI features
ts
export function useCardBackground(color?: string) {
  return {
    background: color ? `linear-gradient(135deg, ${color}DD 0%, ${color}33 100%)` : undefined,
  }
}
  1. Add custom card background to @/pages/sandbox.vue
vue
<template>
  <v-card :style="useCardBackground('#00FF00')">
    <!-- ... -->
  </v-card>
</template>

<script setup lang="ts">
// ...
import { useCardBackground } from '@/composables/ui'
// ...
</script>

This function will provide possibility to dynamically adjust object backgrounds.

UI Component Framework

Custom components

Although each page in Vue is typically a UI component, it is crucial to distinguish between pages, modules specific components and generic, universal, reusable components.

Recommendation is to keep number of components low and rely as much as possible on framework. Vuetify offers vast majority of needed ready to use components. However, custom components are justifiable to create really beneficial and reusable super-features of the app where framework does not provide ones.

Check the Advanced UI Components section.