vuetify

Build a To-do List App with Vuetify #2 – Displaying the List of Tasks


Welcome back to our ongoing tutorial series, where we’re going to build a todo app from start to finish using Vuetify.js. In our last episode, we started by creating the toolbar of the app. Today we’re going to be displaying data and adding some interactivity.

Just getting started with Vuetify?

Creating Sample Tasks for the List

First, let’s make some sample tasks to populate the list we’ll create. In a later tutorial, we’ll let users be able to add tasks by themselves, but for now, this sample data will have to do:

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3" dark rounded="0">
        <v-toolbar-title>Tasks</v-toolbar-title>
      </v-toolbar>
    </v-card>
  </v-app>
</template>

<script>
export default {
  name: 'App',
  data: () => ({
    tasks: [...Array(10)].map((value, index) => ({
      id: `task${index + 1}`,
      title: `Task ${index + 1}`,
      note: `Some things to note about task ${index + 1}`,
    })),
  }),
};
</script>

We use the JavaScript array map() method to automatically generate a list of 10 sample tasks, each with a unique ID, title, and note.

Displaying the List of Tasks

We’ll show the task list using the v-list component and some other sub-components. v-list is wrapped in a v-card component.

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3" dark rounded="0">
        <v-toolbar-title>Tasks</v-toolbar-title>
      </v-toolbar>
    </v-card>
    <v-card>
      <v-list>
        <v-list-item v-for="(task, index) in tasks" :key="index">
          <v-list-item-content
            ><v-list-item-title>{{ task.title }}</v-list-item-title>
          </v-list-item-content>
        </v-list-item>
      </v-list>
    </v-card>
  </v-app>
</template>
...

Using the v-for directive, we loop through tasks and show a v-list-item for each array element. To display content inside each list we use the v-list-item-content component. And then we use v-list-item-title to display the title of the list item, which in this case is the task title.


Get the Source Code for this App

Sign up here to receive the latest source code for this great app!


Showing the Task Notes

The v-list-item component in Vuetify is of three variants: single-line, two-line and three-line, of which the single-line variant is the default. We need to show the note for each task, so let’s set the two-line variant:

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3" dark rounded="0">
        <v-toolbar-title>Tasks</v-toolbar-title>
      </v-toolbar>
    </v-card>
    <v-card>
      <v-list>
        <v-list-item v-for="(task, index) in tasks" :key="index" two-line>
          <v-list-item-content
            ><v-list-item-title>{{ task.title }}</v-list-item-title>
            <v-list-item-subtitle>{{ task.note }}</v-list-item-subtitle>
          </v-list-item-content>
        </v-list-item>
      </v-list>
    </v-card>
  </v-app>
</template>
...

Look closely at the toolbar — it’s flat! Didn’t we put some elevation on it in our last tutorial?

The toolbar appears flat now because the card containing the task list is just right below it. Let’s put some spacing between them, using one of Vuetify’s margin classes.

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3" dark rounded="0">
        <v-toolbar-title>Todos</v-toolbar-title>
      </v-toolbar>
    </v-card>
    <v-card class="mt-4">
      <v-list>
        <v-list-item v-for="(todo, index) in todos" :key="index" two-line>
          <v-list-item-content
            ><v-list-item-title>{{ todo.title }}</v-list-item-title>
            <v-list-item-subtitle>{{ todo.note }}</v-list-item-subtitle>
          </v-list-item-content>
        </v-list-item>
      </v-list>
    </v-card>
  </v-app>
</template>
...

mt-4 is the margin class I was referring to. The m stands for margin, and the t stands for the top. The 4 refers to one of the 32 different margin sizes in Vuetify.

Let’s also set left, right, and bottom margins on the card component all to the same size 4:

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3" dark rounded="0">
        <v-toolbar-title>Tasks</v-toolbar-title>
      </v-toolbar>
    </v-card>
    <v-card class="mt-4 ml-4 mr-4 mb-4">
      <v-list>
        <v-list-item v-for="(task, index) in tasks" :key="index" two-line>
          <v-list-item-content
            ><v-list-item-title>{{ task.title }}</v-list-item-title>
            <v-list-item-subtitle>{{ task.note }}</v-list-item-subtitle>
          </v-list-item-content>
        </v-list-item>
      </v-list>
    </v-card>
  </v-app>
</template>
...

But here’s a better and shorter way to do this:

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3" dark rounded="0">
        <v-toolbar-title>Tasks</v-toolbar-title>
      </v-toolbar>
    </v-card>
    <v-card class="ma-4">
      <v-list>
        <v-list-item v-for="(task, index) in tasks" :key="index" two-line>
          <v-list-item-content
            ><v-list-item-title>{{ task.title }}</v-list-item-title>
            <v-list-item-subtitle>{{ task.note }}</v-list-item-subtitle>
          </v-list-item-content>
        </v-list-item>
      </v-list>
    </v-card>
  </v-app>
</template>
...

As you guessed correctly, the ma-4 class will set a margin of size 4 on the card in all directions.

And here’s how our app should look like right now:

Our toolbar elevation is back, and the card containing our list stands out clearer, with the margins we’ve added.

Indicating Task Completion with Checkboxes

The essence of creating a task is actually getting it done, so let’s show a checkbox in the list for each task that indicates its completion status. We’ll do this with the v-checkbox component.

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3" dark rounded="0">
        <v-toolbar-title>Tasks</v-toolbar-title>
      </v-toolbar>
    </v-card>
    <v-card class="ma-4">
      <v-list>
        <v-list-item v-for="(task, index) in tasks" :key="index" two-line>
          <v-checkbox hide-details v-model="task.isCompleted"></v-checkbox>
          <v-list-item-content>
            <v-list-item-title>{{ task.title }}</v-list-item-title>
            <v-list-item-subtitle>{{ task.note }}</v-list-item-subtitle>
          </v-list-item-content>
        </v-list-item>
      </v-list>
    </v-card>
  </v-app>
</template>

<script>
export default {
  name: 'App',
  data: () => ({
    tasks: [...Array(10)].map((value, index) => ({
      id: `task${index + 1}`,
      title: `Task ${index + 1}`,
      note: `Some things to note about task ${index + 1}`,
      isCompleted: false,
    })),
  }),
};
</script>

Notice we created a new isCompleted property for each task, set to false by default. Using v-model we created a two-way binding between the checkbox and isCompleted.

Our checkbox shows now, but its position is a little off. Let’s fix this with some of the margin classes we used earlier:

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3" dark rounded="0">
        <v-toolbar-title>Tasks</v-toolbar-title>
      </v-toolbar>
    </v-card>
    <v-card class="ma-4">
      <v-list>
        <v-list-item v-for="(task, index) in tasks" :key="index" two-line>
          <v-checkbox hide-details v-model="task.isCompleted" class="mt-0 mr-2"></v-checkbox>
          <v-list-item-content>
            <v-list-item-title>{{ task.title }}</v-list-item-title>
            <v-list-item-subtitle>{{ task.note }}</v-list-item-subtitle>
          </v-list-item-content>
        </v-list-item>
      </v-list>
    </v-card>
  </v-app>
</template>
...

It looks much better now, as we’ve removed the top margin (by setting it to 0) and set the right margin to size 2 in Vuetify.

Using Color to Indicate Task Completion

Apart from using a checkmark to show that a task is done, let’s also change the background color and opacity of the list item to indicate completion, using Vue’s conditional class binding syntax:

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3" dark rounded="0">
        <v-toolbar-title>Tasks</v-toolbar-title>
      </v-toolbar>
    </v-card>
    <v-card class="ma-4">
      <v-list>
        <v-list-item
          v-for="(task, index) in tasks"
          :key="index"
          v-bind:class="{ 'task-completed': task.isCompleted }"
          two-line
        >
          <v-checkbox
            hide-details
            v-model="task.isCompleted"
            class="mt-0 mr-2"
          ></v-checkbox>
          <v-list-item-content>
            <v-list-item-title>{{ task.title }}</v-list-item-title>
            <v-list-item-subtitle>{{ task.note }}</v-list-item-subtitle>
          </v-list-item-content>
        </v-list-item>
      </v-list>
    </v-card>
  </v-app>
</template>

<script>
...
</script>

<style scoped>
.task-completed {
  background-color: #d8d8d8;
  opacity: 0.6;
}
</style>

To Be Continued…

Today we learned how lists work in Vuetify, and we were able to use it to display a sample list of tasks in the app. We also utilized some ready-made Vuetify classes meant for setting the margins of elements. Stay tuned for our next episode, as together we build this to-do list app all the way from start to finish using this fantastic Material Design framework.

Build a To-do List App with Vuetify #1 – Toolbars

Welcome to this brand new tutorial series, where we’re going to be creating a to-do list app all the way from start to finish using the popular Vuetify.js UI library. You can create a new Vuetify project now and follow along if you want. By the time we’re done, you should have a broader knowledge of the features offered by the framework. We’ll go through a notable amount of Vuetify components, including toolbars, cards, forms, lists, navigation drawers and more.

Just getting started with Vuetify?

Creating the Toolbar

Vuetify uses the v-toolbar component for toolbars. To create one, we’ll need to wrap it in a card component — although we can also use it in conjunction with the v-navigation-drawer component.

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar></v-toolbar>
    </v-card>
  </v-app>
</template>
...

Changing the Toolbar Colour

Similar to some other UI libraries, Vuetify uses themes to specify styles that affect all its components. In the following code, we set the colour of the toolbar to primary, which is a colour determined by Vuetify theme settings. We haven’t customized the theme, so this colour will be the default blue.

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary"></v-toolbar>
    </v-card>
  </v-app>
</template>
...

Get the Full Source Code for This App

Sign up here to receive the latest source code for this great app!


Changing the Toolbar Elevation

We can change the amount of elevation the toolbar has, by adjusting the aptly named elevation property. Let’s set it to 3:

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3"></v-toolbar>
    </v-card>
  </v-app>
</template>
...

Let’s set the toolbar title using the v-toolbar-title component inside of v-toolbar :

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3">
        <v-toolbar-title>Tasks</v-toolbar-title>
      </v-toolbar>
    </v-card>
  </v-app>
</template>

Applying the Dark Theme Variant

The title shows now, but observe that its colour is black. We should make it white so it stands out clearer against the blue toolbar background by using the dark property to apply Vuetify’s dark theme variant to v-toolbar .

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3" dark>
        <v-toolbar-title>Tasks</v-toolbar-title>
      </v-toolbar>
    </v-card>
  </v-app>
</template>
...

Many other Vuetify components also have this property, to allow them to be changed to their dark mode. Certain colours of different parts of the component might change, depending on the component. For v-toolbar-title, the colour of any text inside of it becomes white, as you can see:

Removing the Toolbar Roundness

Looking closely at the toolbar, you’ll see that it has a little curvature at each of its corners. This curvature is controlled by the rounded property. We’ll remove the roundness by setting rounded to 0:

src/App.vue

<template>
  <v-app>
    <v-card>
      <v-toolbar color="primary" elevation="3" dark rounded="0">
        <v-toolbar-title>Tasks</v-toolbar-title>
      </v-toolbar>
    </v-card>
  </v-app>
</template>
...

To Be Continued…

We’re off to a great start! Let’s move on to our next episode in this series, where we learn how to use lists, margins and checkboxes to display a list of tasks and add interactivity to our new app.

Create a Colorful Rainbow Carousel App with Vuetify

Here’s what we’ll be creating today:

You’ll see how easy it is to build something like this, with the awesome power of Vuetify.

Just getting started with Vuetify?

Displaying the Colors

To start, we’ll display the colors. We’ll create a colors variable, initialized to an array of all the colors of the rainbow — red, orange, yellow, green, blue, indigo, and violet.

To create the carousel we’ll use the v-carousel component. We’ll use v-carousel-item to add a slide to the carousel. Using the v-for directive we loop through each color in colors and add an item to the carousel to show it. We’ll do this by creating a v-sheet component and setting its color property.

src/App.vue

<template>
  <v-app>
    <v-carousel>
      <v-carousel-item v-for="color in colors" :key="color">
        <v-sheet :color="color" height="100%" tile>
        </v-sheet>
      </v-carousel-item>
    </v-carousel>
  </v-app>
</template>
<script>
export default {
  name: 'App',
  data: () => ({
    carousel: 0,
    colors: ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', '#7f00ff'],
  }),
};
</script>

Showing the words

Now let’s display the sentence. It’s a 7-word sentence, so each word is displayed in one slide. We’ll position the word at the vertical and horizontal centre of the slide using v-row. With the JavaScript string split() method, we’ll divide the sentence into an array of the seven words.

src/App.vue

<template>
  <v-app>
    <v-carousel>
      <v-carousel-item v-for="(color, i) in colors" :key="color">
        <v-sheet :color="color" height="100%" tile>
          <v-row class="fill-height" align="center" justify="center">
            <div class="text-h2">
              {{ words[i] }}
            </div>
          </v-row>
        </v-sheet>
      </v-carousel-item>
    </v-carousel>
  </v-app>
</template>
<script>
export default {
  name: 'App',
  data: () => ({
    carousel: 0,
    colors: ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', '#7f00ff'],
    words: 'These are the colours of the rainbow'.split(' '),
  }),
};
</script>

Changing the Delimiter Icon

Look at the icons that indicate each slide. They’re round. Let’s change them to short rectangles each, by changing the delimiter-icon property of the carousel to mdi-minus:

src/App.vue

<template>
  <v-app>
    <v-carousel delimiter-icon="mdi-minus">
      <v-carousel-item v-for="(color, i) in colors" :key="color">
        <v-sheet :color="color" height="100%" tile>
          <v-row class="fill-height" align="center" justify="center">
            <div class="text-h2">
              {{ words[i] }}
            </div>
          </v-row>
        </v-sheet>
      </v-carousel-item>
    </v-carousel>
  </v-app>
</template>
...

Displaying Arrows on Hover

Currently, the carousel arrows are always displayed. Let’s make them visible only when the user hovers over them with the mouse:

src/App.vue

<template>
  <v-app>
    <v-carousel delimiter-icon="mdi-minus" show-arrows-on-hover>
      <v-carousel-item v-for="(color, i) in colors" :key="color">
        <v-sheet :color="color" height="100%" tile>
          <v-row class="fill-height" align="center" justify="center">
            <div class="text-h2">
              {{ words[i] }}
            </div>
          </v-row>
        </v-sheet>
      </v-carousel-item>
    </v-carousel>
  </v-app>
</template>
...

Hiding the delimiter background

You can see there’s a grey rectangle at the bottom of the carousel. This is the background of the delimiters. We don’t want this rectangle to be visible so let’s set the hide-delimiter-background property to hide it:

src/App.vue

<template>
  <v-app>
    <v-carousel
      delimiter-icon="mdi-minus"
      show-arrows-on-hover
      hide-delimiter-background
    >
      <v-carousel-item v-for="(color, i) in colors" :key="color">
        <v-sheet :color="color" height="100%" tile>
          <v-row class="fill-height" align="center" justify="center">
            <div class="text-h2">
              {{ words[i] }}
            </div>
          </v-row>
        </v-sheet>
      </v-carousel-item>
    </v-carousel>
  </v-app>
</template>
...

Setting Automatic Transitions

To make the carousel automatically transition to the next slide without having to click the right arrow, let’s set the cycle property. The interval determines the amount of time the carousel remains on one of its items before moving on to the next. Here we set an interval of one second.

src/App.vue

<template>
  <v-app>
    <v-carousel
      delimiter-icon="mdi-minus"
      show-arrows-on-hover
      hide-delimiter-background
      cycle
      interval="1000"
    >
      <v-carousel-item v-for="(color, i) in colors" :key="color">
        <v-sheet :color="color" height="100%" tile>
          <v-row class="fill-height" align="center" justify="center">
            <div class="text-h2">
              {{ words[i] }}
            </div>
          </v-row>
        </v-sheet>
      </v-carousel-item>
    </v-carousel>
  </v-app>
</template>
...

Changing the Transition

We can also change the animation the carousel uses when it wants to switch out the item it is currently displaying. By default, the current item slides out to the left as the next one simultaneously slides in. Let us make the current item fade out instead, while the incoming one fades in. This is the fade-transition we set to the transition property of the v-carousel-item. We’ll also do the same for the reverse transition — the current slide will fade out as the previous one fades in.

<template>
  <v-app>
    <v-carousel
      delimiter-icon="mdi-minus"
      show-arrows-on-hover
      hide-delimiter-background
      cycle
      interval="1000"
    >
      <v-carousel-item
        v-for="(color, i) in colors"
        :key="color"
        transition="fade-transition"
        reverse-transition="fade-transition"
      >
        <v-sheet :color="color" height="100%" tile>
          <v-row class="fill-height" align="center" justify="center">
            <div class="text-h2">
              {{ words[i] }}
            </div>
          </v-row>
        </v-sheet>
      </v-carousel-item>
    </v-carousel>
  </v-app>
</template>
...

And just like that, we’ve made ourselves a nice rainbow carousel app with Vuetify!

Getting started with Vuetify

Designing a great user interface from scratch can take a lot of effort. Apart from the skill required for constructing a delightful user experience, you’ll have to worry about things like creating your own design system —with its own icons, colour combinations, typography, etc. while ensuring that the system is fairly consistent with what your users are used to so they don’t get disoriented with the style. Of course, you’ll also have to remember to make sure your apps are mobile responsive so they remain functional and aesthetically pleasing on devices of all sizes.

Luckily there are tons of CSS UI libraries that help simplify this process and work with different JavaScript frameworks. For Vue.js in particular, Vuetify is one of the most popular frameworks out there that you can use to quickly build good-looking and responsive applications.

Introducing Vuetify

Vuetify is an open-source UI component library that allows you to create web apps with stunning visuals without much effort. You can easily add common UI features such as carousels, toolbars, navigation drawers, dialogs, cards, and tables, that already look great to your app. such It has over 33,000 stars on GitHub and is frequently updated by developers. It is based on the well-known Material Design system developed by Google and used by the company in practically all its apps and websites. As stated on its official website:

Material is an adaptable system of guidelines, components, and tools that support the best practices of user interface design. Backed by open-source code, Material streamlines collaboration between designers and developers, and helps teams quickly build beautiful products.

That says it all. Using Material Design for your websites and apps would be a smart move indeed. It has been the design language of every major non-customized version of Android since Android Lollipop (5.1), which was released as far back as 2014. Because of its popularity, adopting the Material Design guidelines will give your apps a consistent design that a huge number of people out there are already familiar with.

Creating the app

In this tutorial, we’re going to be getting started with Vuetify by creating a simple Vue.js project which makes use of some features the framework provides. Here’s what our homepage is going to look like when we’re done:

A sample app created with Vuetify.

When the user enters a message and clicks the Shout button, a notification will appear with a shouty form of the message.

Creating a new project

To get started, use the Vue CLI to create a new Vue project in a shell window. Let’s call it vuetify-app:

vue create vuetify-app

Let’s move to our project directory in the shell using this command:

cd vuetify-app

Let’s add Vuetify to our project using the Vue CLI. While there are other ways to include Vuetify in a Vue.js app, this is one of the easiest and most straightforward:

vue add vuetify

When asked to choose a preset, choose Default. This preset is okay and recommended for most projects, including the one we’ll be building.

Choosing a Vuetify preset.

After installing Vuetify, run the project using npm start or yarn start and navigate to localhost:8080 in a new tab in your browser. You’ll see that the standard Vue.js boilerplate has been altered to the one of Vuetify:

A web page made with the standard Vuetify app boilerplate.

Let’s open up src/App.vue in our project directory and clear out this boilerplate until we’re left with this:

src/App.vue

<template>
  <v-app>
    <v-main>
    </v-main>
  </v-app>
</template>

<script>
export default {
  name: 'App',
  data: () => ({
    //
  }),
};
</script>

The v-app component is now the root of our application. It replaced the default Vue entry point (which was <div id=”app”> in the Vue.js boilerplate). The v-main component is supposed to serve as the root of the main content of our app — just like the main HTML element.

One thing you notice right away is the v- prefix in the component names. All the components that come from Vuetify have this prefix. This is the naming convention used to indicate that the components are part of its library, similar to how custom directives are named in Vue.

Let’s add a text field to the app, using the v-text-field component. We’ll use the label property to set a placeholder value. We also create a message property for a two-way input binding with the text field, so whenever a user edits the text in the text field, message will be updated accordingly. This variable will come in handy for eventually displaying the notification.

src/App.vue

<template>
  <v-app>
    <v-main>
      <v-container>
        <v-text-field label="Message" v-model="message"></v-text-field>
      </v-container>
    </v-main>
  </v-app>
</template>

<script>
export default {
  name: 'App',
  data: () => ({
    message: '',
  }),
};
</script>

Notice we wrapped v-text-field in a v-container component. v-container adds some padding between its children components and other components outside it.

Creating a text field in the Vuetify app.

Adding the Button and the Snackbar

Next, let’s add a button using the v-btn component. We’ll center this element by wrapping it in a div and assigning a text-center class to the div. This class is made available by Vuetify for centering text, as the name implies. We’ll supply a method named shout() to handle clicks on the button.

We’ll also add a snackbar for the notifications using the v-snackbar component. In the shout() click handler, we’ll set a new showSnackbar property to true. v-snackbar is bound to this property. Setting it to true will make the snackbar pop up. After a few seconds, v-snackbar will set this variable back to false so the snackbar can be hidden again.

src/App.vue


<template>
  <v-app>
    <v-main>
      <v-container>
        <v-text-field label="Message" v-model="message"></v-text-field>
        <div class="text-center">
          <v-btn color="primary" @click="shout">Shout</v-btn>
        </div>
      </v-container>
    </v-main>
    <v-snackbar v-model="showSnackbar"></v-snackbar>
  </v-app>
</template>
<script>
export default {
  name: 'App',
  data: () => ({
    message: '',
    showSnackbar: false,
  }),
  methods: {
    shout() {
      this.showSnackbar = true;
    },
  },
};
</script>
Add the button and the snackbar to the Vuetify app.

Displaying the Shouty Message

Right now, clicking the button just displays an empty notification. To make the snackbar show the shouty version of the message the user entered, we’ll create a new variable, loudMessage . In the shout() click handler, we’ll set this variable to the capitalized form of the value of message using the JavaScript toUpperCase() string method, and concatenate it with three exclamation marks. We’ll make this message a text child of the v-snackbar element.

src/App.vue

<template>
  <v-app>
    <v-main>
      <v-container>
        <v-text-field label="Message" v-model="message"></v-text-field>
        <div class="text-center">
          <v-btn color="primary" @click="shout">Shout</v-btn>
        </div>
      </v-container>
    </v-main>
    <v-snackbar v-model="showSnackbar">{{loudMessage}}</v-snackbar>
  </v-app>
</template>
<script>
export default {
  name: 'App',
  data: () => ({
    message: '',
    showSnackbar: false,
    loudMessage: '',
  }),
  methods: {
    shout() {
      this.loudMessage = `${this.message.toUpperCase()}!!!`;
      this.showSnackbar = true;
    },
  },
};
</script>

Changing the Snackbar Transition and Position

Let’s customize the snackbar. Currently, it fades in at the bottom centre of the screen. Let’s make it slide in from the bottom left by modifying some properties of the element. There are many other values we could set the transition property to, but for our purposes, we need to set it to slide-y-reverse-transition . To move the snackbar to left we’ll set the left property to true. Notice we use a colon (: ) before left so that true gets evaluated as the boolean value true, instead of the string “true”.

src/App.vue
<template>
  <v-app>
    <v-main>
      <v-container>
        <v-text-field label="Message" v-model="message"></v-text-field>
        <div class="text-center">
          <v-btn color="primary" @click="shout">Shout</v-btn>
        </div>
      </v-container>
    </v-main>
    <v-snackbar
      v-model="showSnackbar"
      transition="slide-y-reverse-transition"
      :left="true"
      >{{ loudMessage }}</v-snackbar
    >
  </v-app>
</template>
...

And our Vuetify app is complete!

Even with this small app, I hope you’ve been able to see for yourself how much work UI libraries like Vuetify can save you. Without using any CSS or custom styles we’ve been able to design a nice-looking and fully functional web app in a short time.