Vue.js has been a JavaScript framework that has been gaining more and more popularity on the market for several years. Based on components, it focuses primarily on the logic of user interaction with the application. Therefore, to easily create an attractive UI, it is best to use an external library. Currently available on the market are, among others:
- Quasar – an open-source framework that allows you to build stable, configurable, applications based on Material Design or an iOS theme.
- UI element – a library designed to build UI in desktop applications,
- BootstrapVue – implementation of components of probably the world’s most popular CSS Bootstrap V4 library and grid system for Vue.
- Vuetify – a framework based on reusable components developed under the Material Design standard. This is what we will focus on today. We will check its advantages, how to add it to the application, and create an example application.
In this article, I will try to introduce you to work with Vuetify. Together, we will go through the library installation and configuration process and build a sample UI.
WHY CHOOSE VUETIFY?
Vuetify is compatible with Vue CLI 3, which means easy setup and the ability to add a library at any stage of project development. It has support for all modern browsers. It also offers a basic template for HTML, Webpack, NUXT, PWA, Electron, A La Carte, and Apache Cordova. Working with the framework is very pleasant, largely due to the transparent and comprehensive documentation.
Interactive usage examples that update the code ready to be copied allow you to quickly apply the selected version of the component in the application.

The list of examples is very long. In addition to the source code, each of them contains a link to freely maneuver the code in Codepen and to download the solution from GitHub.

The whole is complemented by an extensive API listing all:
- props – defining individual component settings
- slots – available in a given component
- events – thrown by the component
- functions – that can be called on it
- Sass variables – styling component

INSTALLATION
We will initiate the project together with Vuetify using Vue CLI 3, but this is not the only option. It can also be done via Nuxt or Webpack, which is described in detail in the documentation. We will use npm as the package manager. So if you don’t already have node installed, download and install the latest version.
We start with the installation of the Vue CLI
npm install -g @vue/cli
Then we create a project
vue create school-manager
We will be asked about configuration options. Let’s leave the default options everywhere.

After the installation is complete, go to the project and install Vuetify in it.
cd school-manager
vue add vuetify
Here we also choose the default option.

Now we can run the application with the npm run serve command. After starting, our eyes should see the start page of the project.

APPLICATION CONSTRUCTION
Let’s build a sample application to manage the school database.
First, let’s clear the HelloWorld.vue file, because we will not use the start page elements and rename it to something more suitable for the application, eg PupilsManagement.vue. Besides renaming the file itself, remember to change the reference and import of the component in the App.vue file.

Before starting the construction of individual elements of the application, we will set things that we will use in almost every element of the application: colors and font.
1. COLOR THEMES
Thanks to Vuetify, we can use light and dark modes in the application. The library uses the default set of colors to stylize the components, as we had in the generated page HelloWorld. Defining colors for individual themes in Vuetify has another advantage. Because it is set in one place, it allows you to change the color of the application without having to replace the color in individual components.
We want to dress the application in the colors of the school. So we have to overwrite the default settings. To do this, in the src/plugins/vuetify.js folder in the Vuetify object, add the theme object as below. Adding the code below will overwrite the colors in the light theme.
import Vue from 'vue';
import Vuetify from 'vuetify/lib/framework';
Vue.use(Vuetify);
export default new Vuetify({
theme: {
themes: {
light: {
primary: '#cfdac8',
primaryLight: '#ffffcb',
primaryDark: '#cdd0cb',
secondary: '#fcda39',
secondaryLight: '#f6f4e6',
secondaryDark: '#7c9473'
},
},
},
});
2. SASS VARIABLES – CHANGING THE DEFAULT SETTINGS
The default font used by Vuetify is Roboto. Let’s assume that the font used in the school is Lato and use it in the application.
We create the variables.scss file in the project’s src/styles folder. In the variables file, we will change the default font by overwriting the appropriate global variable sass $body-font-family.
$body-font-family: 'Lato', sans-serif;
A list of all global variables provided by Vuetify can be found here. Apart from global variables, there are also variables defined for individual components, e.g. for the v-list component.
We cannot forget to import the font in the head section of index.html. We can download the import code from Google Fonts.
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;1,100;1,300;1,400&display=swap" rel="stylesheet">
3. APPLICATION NAVIGATION
We will be the first to create the header of the application, in which we will place its title and navigation. Suppose we want to have access to five subpages: students, teachers, classes, lesson plan, and grade book.
In App.vue above <v-main> we add a specially created component Vuetify <v-app-bar>. We add props:
- app – for the header to be able to create a page layout element
- color=”primaryDark” – giving the previously defined color as background-color
- class=”px-10″ – applying 40px padding on the left and right side of the header
Inside the app bar, we put the title <v-toolbar-title>. We assign it class=”text-h4″ to make it the right size.
<template>
<v-app>
<v-app-bar app color=”primaryDark” class="px-10">
<v-toolbar-title class="text-h4">Aplikacja zarządzająca bazą szkoły XYZ</v-toolbar-title>
</v-app-bar>
<v-main>
<PupilsManagement/>
</v-main>
</v-app>
</template>
Below the title, we add 5 text buttons. We classify it as font-weight-bold to make the font slightly bold.
<v-btn class="font-weight-bold" text>Uczniowie</v-btn>
<v-btn class="font-weight-bold" text>Nauczyciele</v-btn>
<v-btn class="font-weight-bold" text>Klasy</v-btn>
<v-btn class="font-weight-bold" text>Plan lekcji</v-btn>
<v-btn class="font-weight-bold" text>Dziennik</v-btn>
We put a <v-spacer> component between the title and the buttons, which will fill the space between them and move them to the left and right. The entire App.vue template looks like this:
<template>
<v-app>
<v-app-bar app color=primaryDark class="px-10">
<v-toolbar-title class="text-h4">Aplikacja zarządzająca bazą szkoły XYZ</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn class="font-weight-bold" text>Uczniowie</v-btn>
<v-btn class="font-weight-bold" text>Nauczyciele</v-btn>
<v-btn class="font-weight-bold" text>Klasy</v-btn>
<v-btn class="font-weight-bold" text>Plan lekcji</v-btn>
<v-btn class="font-weight-bold" text>Dziennik</v-btn>
</v-app-bar>
<v-main>
<PupilsManagement/>
</v-main>
</v-app>
</template>
And the rendered header looks like this:

4. RESPONSIVENESS
The school database management application is a desktop application, however, we must prepare for different screen widths. Vuetify has a built-in 12-step Grid system that allows you to adjust the size and arrangement of the element depending on the width of the screen. Implements breakpoints compliant with the Material Design specification, which we can use for conditional display and styling of components.

In addition to the Grid system, Vuetify also provides auxiliary classes such as display or text. By operating with the breakpoint and value, we can e.g. change the size of the text depending on the screen size.
.text-{breakpoint}-{value}
By using the text and display auxiliary class, let’s make the page title not displayed on tablets and smartphones. Additionally, the text will be slightly smaller on displays less than 1200px wide. Vuetify applies features from the narrowest to the broadest. So we need to change the class from text-h4 to text-lg-h4 and add the class text-h6. The text size of h6 will be applied to devices with width < lg, and h4 to >= lg.
We do the same with the display. We are adding class d-none to make the title invisible on mobile. Then we add the class d-md-inline to appear at widths >= md.
Currently, the toolbar title implementation looks like this:
<v-toolbar-title class="text-h6 text-lg-h4 d-none d-md-inline">Aplikacja zarządzająca bazą szkoły XYZ</v-toolbar-title>
5. HOME PAGE
The PupilsManagement.vue component will manage the main view of the Students tab. Here we will include a table showing student data 2/3 the width of the page and a menu of buttons 1/3 the width of the page. So let’s create a skeleton.
<template>
<v-container>
<v-row class="mt-6">
<v-col cols=8>
</v-col>
<v-col cols=4>
</v-col>
</v-row>
</v-container>
</template>
6. TABLE OF PUPILS
Suppose we want to display the first and last name of the student and the class they are attending in a table. In addition, we want to be able to remove the student from here and go to edit his data.
We add sample data to the data object.
data: () => ({
headers: [
{
text: 'imię',
value: 'name'
},
{
text: 'nazwisko',
value: 'secondName'
},
{
text: 'klasa',
value: 'class'
},
{
text: '',
value: 'actions'
}
],
pupils: [
{
name: 'Ewelina',
secondName: 'Radomska',
class: 'IVa',
},
{
name: 'Karol',
secondName: 'Krakowski',
class: 'IIc',
},
{
name: 'Julia',
secondName: 'Tracz',
class: 'VIIb',
},
{
name: 'Krystian',
secondName: 'Gromelski',
class: 'Ia',
},
{
name: 'Igor',
secondName: 'Jankowski',
class: 'Vc',
},
{
name: 'Kacper',
secondName: 'Świerszcz',
class: 'Vc',
},
{
name: 'Alicja',
secondName: 'Olapa',
class: 'IIc',
},
{
name: 'Laura',
secondName: 'Andrzejczyk',
class: 'Ia',
}
],
}),
In the table column, add the <v-data-table> component and pass sample data via probs.
<v-data-table :headers="headers" :items="pupils" class="elevation-1"></v-data-table>
We will run the editing and deletion of a student by clicking the button on each of the lines. To display the buttons in a given column, we need to refer to the slot item. <Column name> available in the <v-data-table> component. For this purpose, we select the actions column. We put in it 2 icon type buttons.
<template v-slot:item.actions>
<v-btn small icon outlined>
<v-icon small>mdi-pencil</v-icon>
</v-btn>
<v-btn small icon outlined>
<v-icon small>mdi-delete</v-icon>
</v-btn>
</template>
Students are assigned to classes. It would be easier to browse the table if it had two levels and grouped students by grade. We will add prop group-by=”class” to <v-data-table>.
At this point, the table looks like this:

We will use the group.header slot to change the group row. We only want the name of the group in it, i.e. the class, and the button to open/close the group. Inside the table we add:
<template v-slot:group.header="{ group, headers, toggle, isOpen }">
<td :colspan="headers.length">
<v-btn @click="toggle" small icon :data-open="isOpen">
<v-icon v-if="isOpen">mdi-chevron-up</v-icon>
<v-icon v-else>mdi-chevron-down</v-icon>
</v-btn>
{{ group }}
</td>
</template>
In the interior, we use elements provided by the slot:
- group – that returns the name of the group, just used for display
- headers – passing the array of table headers necessary to stretch the row across the width of the table
- isOpen – flag informing about group opening used to change the button icon depending on the group opening
- toggle – the slot method responsible for opening and closing a group
The table gained the group’s lines as we planned. It would be worth equipping it with a button that closes and opens all groups at once.

We will place the button above the table, or rather in its upper year. In the column with the table, we add 2 rows. Paste the table into the bottom one. We set the top one to 0px because we don’t want him to move the table down. It is intended for button positioning only.
Inside the poem, we put the already known <v-spacer> and <v-tooltip> headers. The Tooltip is visible when you hover over the button. We achieve this effect through the slot: activator=”{on}”.
Row with button:
<v-row style="height: 0px">
<v-spacer></v-spacer>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-btn fab color="secondaryDark" @click="toggleAll()" class="top-table ma-2" v-on="on">
<v-icon dark>mdi-unfold-more-horizontal</v-icon>
</v-btn>
</template>
<span>Otwórz/zamknij klasy</span>
</v-tooltip>
</v-row>
The button references the toggleAll() method. Its job is to open groups when closed and close when open. Add the methods object to the component, and in it:
toggleAll () {
if(this.toggleOpen) {
this.closeGroup()
this.toggleOpen = false
} else {
this.openGroup()
this.toggleOpen = true
}
},
closeGroup () {
Object.keys(this.$refs).forEach(k => {
let groupButton = this.$refs[k]
if(groupButton.$attrs['data-open']) {
groupButton.$el.click()
}
})
},
openGroup () {
Object.keys(this.$refs).forEach(k => {
let groupButton = this.$refs[k]
if(!groupButton.$attrs['data-open']) {
groupButton.$el.click()
}
})
},
The toggleAll() method uses the toggleOpen switch. So we have to add it to the data object with the value true or false depending on whether the classes are to be open or closed by default. Each of the closeGroup() and openGroup() methods iterate through groups. We need to add a unique reference to each of the class opening/closing buttons. Thanks to this, we will be able to trigger a click on them. So we add :ref=”group”
to the button inside the group.header slot.
7. USE COLORS FROM THEMES IN <STYLE> BLOCK
So far, we’ve used the colors defined in the light theme in the <template> block. Now we will use them in the <style> block in:
- PupilsManagement.vue to change the background-color of a class line
- App.vue to change the background-color of the application
Setting the customProperties option in Vuetify generates css variables which we can then use in a <style> block.
In App.vue below the <script> block we add:
<style lang="scss">
.theme--light.v-application{
background-color: var(--v-primary-base) !important;
}
</style>
At PupilsManagement.vue:
<style lang="scss">
.v-row-group__header {
background-color: var(--v-secondary-base) !important;
font-weight: 700;
font-size: 20px;
}
</style>
Now we can admire the effect of the change.

8. MENU FOR PUPIL MANAGEMENT
In the place previously reserved for the <v-col cols=4> menu, we will add the <v-card> component. Similarly to the table below, we will give it the elevation-1 auxiliary class so that it is at the same level of elevation.
The menu header is enriched with an image with an applied gradient. The image in the src/assets folder should be imported in the <script> section and added to the data object.
<script>
import image from '../assets/colored-pencils-656178_1280.jpg';
export default {
data: () => ({
image: image ,
…
We set it to a fixed height of 100px. We will also use auxiliary classes:
- white-text to make the text inside white
- align-end to set the internals at the bottom of the image
In the <v-card-actions> section, we will add sample buttons and a field to search for students in the table.
<v-col cols=4>
<v-card class="elevation-1">
<v-img :src="image" gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)" height="100px" class="white--text align-end">
<v-card-title>Menu</v-card-title>
</v-img>
<v-card-actions>
<v-container>
<v-row>
<v-col>
<v-text-field label="Wyszukaj" hide-details="auto" v-model="search" color="secondary"></v-text-field>
</v-col>
</v-row>
<v-row class="mt-4">
<v-btn color="secondaryDark" class="ma-2">
Dodaj ucznia
<v-icon right dark>fa-address-book-o</v-icon>
</v-btn>
<v-btn color="secondaryDark" class="ma-2">
Przypisz do klasy
</v-btn>
</v-row>
</v-container>
</v-card-actions>
</v-card>
</v-col>
<v-text-field> in v-model takes the search value, in our case called search. We need to add it to the data object. For table search to work, add props to <v-data-table> :
- :search=”search” – the search value to which we bind the search field from the data object
- :custom-filter=”filterOnlyCapsText” – custom-filter expects a method that filters table elements that returns a boolean
In our case, the filtering method is:

In this way, we have built a view in a simple and quick way, in which it is possible to aesthetically display the student base and filter it. By adding methods to the remaining buttons of the table or menu, we would get the complete functionality of requesting the student base, but this is not the purpose of this article.
SUMMARY
Vuetify allows you to quickly build a good looking UI. Thanks to ready-made components and quite extensive and clear documentation at the same time, it is easy to use and will not cause problems even for a novice programmer. Compliance with the Metarial Design standard means that we do not have to care too much about aesthetics, because it is almost built into the components. This, however, has some drawbacks. Vuetify offers many possibilities to customize the UI to our needs, but it is always done within the Material Design standard. So if we need full customization, it is better to choose BootstrapVue. However, when we can allow ourselves the freedom in choosing the UI or we are looking for a library with the Material Design standard, Vuetify is second to none.