I enjoy the UI-elements that are created to the specification outlined as part of Google's Material Design. I had used Material UI for React in the past, so when I started using Vue more intensely, I found myself looking for a similar framework. The answer to my quest came in the form of Vuetify.
I will be sharing my experience in getting to know what this framework is all about.
Summary
- Project Setup
- App Layout
- UI-Elements
- Final Thoughts
Project Setup
As of time of writing, Vuetify only supports Vue 2.x, which is what I will be using for this tutorial.
Let us first create a vue project using the vue-cli:
vue create vuetify-test
I know, not the most original name, but it is a name.
I am setting this project up with Vue 2.x, as the latest stable release of Vuetify currently does not support Vue 3.x yet.
I am using TypeScript for this project, but you can use whatever you like. Alongside TypeScript, I am also going to be using the class-based syntax for my components.
After the installation has completed, we can cd into the vuetify-test directory and with npm run serve
to check that everything went according to plan.
After completing the project setup it is time to add Vuetify to our freshly installed project:
vue add vuetify
This will use the vue-cli-vuetify-plugin to get us up-and-running.
I will be using the default setup:
That's it!
Very simple installation using the vue-cli.
Now use npm run serve
to check and you should see a big difference:
Our setup is done - we can now have a play around with our code.
App Layout
For the layout I would like to have a side-navigation with links to the different pages.
First, I am going to get rid of all the boilerplate code that comes with installing Vuetify.
Navigation
Using the <v-navigation-drawer app>
we can tell vuetify that we would like a navigation bar on the side. The attribute app
tells Vuetify that this element is part of our layout.
Here is what my App.vue looks like:
<template>
<v-app>
<v-navigation-drawer app>
<v-divider></v-divider>
</v-navigation-drawer>
</v-app>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
@Component
export default class App extends Vue {}
</script>
The @Component
may look a little unfamiliar. This is the class-based syntax that Vue optionally allows through the vue-property-decorator package.
Now I am going to add an avatar and a few links to justify the existence of our navigation.
<template>
<v-app>
<v-navigation-drawer app>
<v-list>
<v-list-item class="d-flex justify-center">
<v-list-item-avatar
color="primary"
class="white--text"
>
UXE
</v-list-item-avatar>
</v-list-item>
</v-list>
<v-divider></v-divider>
<v-list>
<v-list-item link>
<v-list-item-icon>
<v-icon>mdi-email-outline</v-icon>
</v-list-item-icon>
<v-list-item-title>Messages</v-list-item-title>
</v-list-item>
<v-list-item link>
<v-list-item-icon>
<v-icon>mdi-file-sync-outline</v-icon>
</v-list-item-icon>
<v-list-item-title>Shared Files</v-list-item-title>
</v-list-item>
<v-list-item link>
<v-list-item-icon>
<v-icon>mdi-account-outline</v-icon>
</v-list-item-icon>
<v-list-item-title>Contact List</v-list-item-title>
</v-list-item>
<v-list-item link>
<v-list-item-icon>
<v-icon>mdi-archive-outline</v-icon>
</v-list-item-icon>
<v-list-item-title>
Archived Messages
</v-list-item-title>
</v-list-item>
</v-list>
</v-navigation-drawer>
</v-app>
</template>
I was certainly a little overwhelmed when I first saw all of the v-this and v-that. So let us break this down a bit.
The v-list is the first new component we are using in this. We are using this component to display our avatar at the top and then again further down to display our links underneath each other.
The v-list-item
specifies exactly what it says - an item of our list.
On our v-list-item-avatar
we use the color-attribute to specify our background colour of avatar and the class of white--text
tells with what colour we want our text to be.
Between the avatar at the top and the links we have this <v-divider>
which separates them through a horizontal rule.
Each v-list-item
here has a link-attribute - giving them that nice ripple effect when clicking on them.
The v-list-item
also is made up of an icon and a label. The framework makes use of this huge Material Design Icons Library for icons. You will find an icon for every occasion here.
We end up with something like this:
That is our navigation done. Let us separate this into its own component.
For that we can create a new file in the src directory and name it whatever you like - I am going to go with SideNavigation.vue and add in the markup:
<template>
<v-navigation-drawer app>
<v-list>
<v-list-item class="d-flex justify-center">
<v-list-item-avatar color="primary" class="white--text"
>UXE</v-list-item-avatar
>
</v-list-item>
</v-list>
<v-divider></v-divider>
<v-list>
<v-list-item link>
<v-list-item-icon>
<v-icon>mdi-email-outline</v-icon>
</v-list-item-icon>
<v-list-item-title>Messages</v-list-item-title>
</v-list-item>
<v-list-item link>
<v-list-item-icon>
<v-icon>mdi-file-sync-outline</v-icon>
</v-list-item-icon>
<v-list-item-title>Shared Files</v-list-item-title>
</v-list-item>
<v-list-item link>
<v-list-item-icon>
<v-icon>mdi-account-outline</v-icon>
</v-list-item-icon>
<v-list-item-title>Contact List</v-list-item-title>
</v-list-item>
<v-list-item link>
<v-list-item-icon>
<v-icon>mdi-archive-outline</v-icon>
</v-list-item-icon>
<v-list-item-title>Archived Messages</v-list-item-title>
</v-list-item>
</v-list>
</v-navigation-drawer>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
@Component
export default class SideNavigation extends Vue {}
</script>
Now we can add this component in our App.vue.
First import the component at the top, then register it:
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import SideNavigation from "@/components/SideNavigation.vue";
@Component({
components: {
SideNavigation,
},
})
export default class App extends Vue {}
</script>
Finally, you can use it within your template:
<template>
<v-app>
<side-navigation></side-navigation>
</v-app>
</template>
This component does seem rather lonely - let's add some more.
UI-Elements
The Vuetify-team have done a great job in documenting the different ways you can use the beautifully crafted components they have made. I definitely encourage you to have a play around with the different components and see what you can come up with.
Basic Material Design Form
I found the way they make forms very helpful and interesting - therefore I am going to use this as my example for UI-Elements.
Here are docs for forms for those of you who are curious.
Let us create a new file for our signup form - SignUpForm.vue.
The template I will be using will look like this:
<template>
<v-form>
<v-container>
<v-row>
<v-col cols="12" md="6">
<v-text-field
v-model="user.firstName"
:rules="nameRules"
:counter="10"
label="First name"
required
></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="user.lastName"
:rules="nameRules"
:counter="10"
label="Last name"
required
></v-text-field>
</v-col>
<v-col cols="10" md="8">
<v-text-field
v-model="user.email"
:rules="emailRules"
label="E-mail"
required
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="12">
<v-btn block color="green darken-2" class="white--text"
>Submit My Data</v-btn
>
</v-col>
</v-row>
</v-container>
</v-form>
</template>
Which yields the following form:
The v-form
specifies our form container - within that we use the v-container
to add padding on either side.
Inside our v-container
we have our v-rows
and v-cols
. These control how our elements are placed in rows and columns.
If you are familiar with Bootstrap then these concepts shouldn't be totally new to you. The Vuetify Docs themselves state that the framework has been heavily influenced by Bootstrap.
The width of the v-cols
can be controlled by using the cols
and by using md
in this case.
The v-model
will look familiar to you, if you have used Vue before. This is how Vue allows us to manage our data - so handling user input or the data that gets sent from an API and many more.
Our two input fields for the first and last name have two props - :rules
and :counter
.
The :rules
prop checks if an error has occurred and displays the correct error message:
private nameRules = [
(value: string) => !!value || "Field ist required",
(value: string) =>
value.length <= 10 || "Maxmimum of 10 Characters allowed",
];
The :counter
prop, well, counts the number of characters and displays this to the user:
The error states will look like this:
We have 14 instead of the allowed 10 characters in the name.
And if we leave the field blank, we also get an error, because in this case, the field is required:
The full validation rules in my script-tag looks like this:
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
@Component
export default class SignUpForm extends Vue {
private user = {
firstName: "",
lastName: "",
email: "",
};
private nameRules = [
(value: string) => !!value || "Field ist required",
(value: string) =>
value.length <= 10 || "Maxmimum of 10 Characters allowed",
];
private emailRules = [
(value: string) => !!value || "Field is required",
(value: string) => /.+@.+/.test(value) || "E-Mail must be valid",
];
}
</script>
We also have validation rules for our E-Mail-Input - it is a regular expressions that checks if an @-symbol is in the string. If not, it again will display the error message.
Final Thoughts
That is it for this article.
This was a small subsection of what Vuetify has to offer, I can wholeheartedly recommend the documentation if you are interested in using Vuetify in your next project.
The documentation is great because they have different options for you to try out. They have done some really awesome work in making the documentation more interactive and interesting.
You can literally build out a version of your button in the browser, then transfer that into your code:
And then copy the button-markup straight into your project.
If you have used Vuetify or any other Material Design Framework before (does not have to be a Vue-related one) then leave a comment telling me what your experience was.
I'd love to hear from you.
Top comments (0)