Diogo Souza Brazilian dev. Creator of altaluna.com.br

Is Vuetify right for you?

10 min read 2948

Editor’s Note: This post was updated in August 2021 to include relevant information on Vuetify and minor adjustments to code blocks.

The advent of new frameworks for visual component encapsulation in web apps has highlighted several other needs: routing, security, SPA, and PWA support, internationalization, and, of course, UI.

Such is true of Vue.js, which has been widely adopted for web development and hybrid mobile applications. While it has a strong focus on component logic and the application as a whole, there is a consistent need to work with some other type of UI library or framework to pour style and design into the pages.

On the other side, among the numerous UI frameworks that deal with the customization and facilitation of visual components and screen design, there are frameworks that have adopted the famous Material Design standard. The community has widely accepted this option because it embraces both the desktop and mobile universes with responsiveness, flexibility, and extensibility.

Because most developers working with both paradigms are already familiar with their use, why not join the two worlds? Why not, then, join Vue with the Materialize trend? That’s where Vuetify.js was born.

What is Vuetify?

Vuetify complies with the Material Design specification. This means that the core features of both Vue and Material will be available by default and can be improved by both communities. In addition, Vuetify offers:

  • Compatibility with Vue CLI-3 and RTL
  • Templates for various frameworks (Cordova, webpack, etc.)
  • Internationalization
  • SSR and PWA

However, using Vuetify means you want, and will embrace, Material Design. Apps intended for the iOS universe, for example, are not good candidates for Vuetify. Likewise, if you need a custom design in regards to style as a whole, Vuetify may not suit you, either. As always, you still need to perform a prior and deeper analysis of your project’s needs.

To better understand how Vuetify works, and to get a taste of it, take a look at the image below:

Let’s create from scratch a simple, responsive, and Material-based page with elements from LogRocket’s pricing page (the selection of plans in the Pricing menu). Note that the layout of elements is very intuitive, and the card’s template is reminiscent of how Bootstrap works with grids, for example.

Installation and setup with Vuetify

Let’s start by laying out everything we need for the project. First of all, you need to have the latest version of Node.js/npm installed on your machine. For this tutorial, we’ll stick with Node v14.17.4 and npm v6.14.14. You may use nvm to help better manage your Node versions.

We made a custom demo for .
No really. Click here to check it out.

To install the Vue CLI globally, it is important to first check if you have its previous version (the package name changed from vue-cli to @vue/cli). To make sure, try running the following command to globally uninstall the old package:

npm uninstall -g vue-cli

Then, run the following command to install the new right package:

npm install -g @vue/cli

Then, make sure to check what version was installed:

vue --version

The command should print the version as follows:

@vue/cli 4.5.13

The next step is to scaffold our application. We’ll use a series of preset files to make our life easier — like some .vue templates files, index.html, and main.js:

vue create vuetify-logrocket

The installation guide will ask which JavaScript and lint compiler presets you want:

vuetify picking a preset

Let’s leave the default options and continue with an Enter. Wait for the installation to complete, then cd the directory of the created application and run the command to add Vuetify to our project:

cd vuetify-logrocket
vue add vuetify

Wait until the installation takes you to the next selection of presets, this time from Vuetify:

Installing vue-cli-plugin-vuetify

Just leave it as is and wait for the installation to complete. The image below shows the outputs after adding the plugin:

vuetify-right-created-directories

To start the application, simply run the npm run serve command. This will also cause the file changes watcher to start and listen to our edits, updating the pages automatically.

Autogenerated home page

Customizing the Vuetify page

Before proceeding with this article, I’d advise you to read about some of Vuetify’s basic components in its official documentation. This will help facilitate recognition of what we’ll see ahead — we won’t go into each of them in detail.

Because we won’t use any of the content generated by Vuetify in App.vue, you can delete all its contents. We’ll begin by constructing the first part of the screen, among three others:

  • Navigation (toolbar and menus)
  • Body (where we’ll have the plans cards for pricing)
  • Footer

Navigation

Navigation will follow the in-depth menu display pattern when viewed via desktop, along with a navigation drawer common to mobile apps or websites viewed on smartphones (with the hamburger icon template). See how our site will display on an iPhone below:

Page visualization on an iPhone X

First, create a new file named navigation.vue in the /src/components/ folder, and add the following content:

<template>
    <span>
        <v-navigation-drawer app v-model="drawer">
            <v-list>
                <template v-for="(menu, i) in menus">
                    <v-list-tile :key="i">
                        <v-list-tile-content>
                            {{menu.value}}
                        </v-list-tile-content>
                    </v-list-tile>
                    <v-divider :key="`divider-${i}`"></v-divider>
                </template>
            </v-list>
        </v-navigation-drawer>
        <v-toolbar app dark>
            <v-toolbar-side-icon class="hidden-md-and-up" @click="drawer = !drawer"></v-toolbar-side-icon>
            <v-spacer class="hidden-md-and-up"></v-spacer>
            <v-img src="https://assets.logrocket.io/img/logo.png" max-width="120px" max-height="25px"></v-img>
            <v-btn flat class="hidden-sm-and-down">Pricing</v-btn>
            <v-spacer class="hidden-sm-and-down"></v-spacer>
            <v-btn flat class="hidden-sm-and-down">Customers</v-btn>
            <v-btn flat class="hidden-sm-and-down">Docs</v-btn>
            <v-btn flat class="hidden-sm-and-down">Blog</v-btn>
            <v-btn flat class="hidden-sm-and-down">Login</v-btn>
            <v-btn color="purple darken-3" class="hidden-sm-and-down">Sign Up</v-btn>
        </v-toolbar>
    </span>
</template>

<script>
export default {
    name: 'RocketNavigation',
    data() {
        return {
            drawer: false,
            menus: [
                { value: 'Pricing' },
                { value: 'Customers' },
                { value: 'Docs' },
                { value: 'Blog' },
                { value: 'Login' },
                { value: 'Sign Up' }
            ]
        };
    }
};
</script>

<style scoped>
</style>
<template>
    <span>
        <v-navigation-drawer app v-model="drawer">
            <v-list>
                <template v-for="(menu, i) in menus">
                    <v-list-tile :key="i">
                        <v-list-tile-content>
                            {{menu.value}}
                        </v-list-tile-content>
                    </v-list-tile>
                    <v-divider :key="`divider-${i}`"></v-divider>
                </template>
            </v-list>
        </v-navigation-drawer>
        <v-toolbar app dark>
            <v-toolbar-side-icon class="hidden-md-and-up" @click="drawer = !drawer"></v-toolbar-side-icon>
            <v-spacer class="hidden-md-and-up"></v-spacer>
            <v-img src="https://assets.logrocket.io/img/logo.png" max-width="120px" max-height="25px"></v-img>
            <v-btn flat class="hidden-sm-and-down">Pricing</v-btn>
            <v-spacer class="hidden-sm-and-down"></v-spacer>
            <v-btn flat class="hidden-sm-and-down">Customers</v-btn>
            <v-btn flat class="hidden-sm-and-down">Docs</v-btn>
            <v-btn flat class="hidden-sm-and-down">Blog</v-btn>
            <v-btn flat class="hidden-sm-and-down">Login</v-btn>
            <v-btn color="purple darken-3" class="hidden-sm-and-down">Sign Up</v-btn>
        </v-toolbar>
    </span>
</template>

<script>
export default {
    name: 'RocketNavigation',
    data() {
        return {
            drawer: false,
            menus: [
                { value: 'Pricing' },
                { value: 'Customers' },
                { value: 'Docs' },
                { value: 'Blog' },
                { value: 'Login' },
                { value: 'Sign Up' }
            ]
        };
    }
};
</script>

Note that the first tag refers to the definition of a template created in Vue.js. This tag requires that one and only one child element be added to the hierarchy; otherwise, we will receive an error. To bypass this check, we add a <span> (or any other neutral HTML element that encapsulates the others).

We will do this because we need both the <v-navigation-drawer> (for mobile visualization) and <v-toolbar> (desktop visualization) components to coexist.

Let’s look at some other important considerations:

  • The app directive is used in both components so that Vuetify understands they belong to the application at a more global level  —  that is, how to resize and readjust them relative to the parent element
  • v-model defines what type of model we are using  —  in this case, drawer. This element is important so that the list of menus does not appear hanging. The JavaScript that feeds your items is at the end of the listing
  • The rest of your content represents a loop that iterates over the menu elements, predefined in the menus element. Here, we use the <v-list> component to iterate and compose the title sub-elements (<v-list-title>) and divider (<v-divider>)
  • In the <v-toolbar> element, we see the use of the dark directive for Vuetify’s default night style import

The <v-toolbar-side-icon> component represents our hamburger icon. It should, of course, only appear when we are viewing in mobile mode, which is why all elements of the toolbar receive CSS classes for the Material Design Viewport Breakpoints.

This is Vuetify’s default mechanism for controlling the display of elements based on the current viewport. See the official dimensions table for each Vuetify prefix below:

Material Design Viewport Breakpoints

These conditions follow the format hidden-{breakpoint}-{condition}. For example, the hidden-md-and-up class can be translated as follows:

The breakpoint sets the viewport size to md (medium devices), and the condition applies the class based on and-up. In other words, it hides the element on the specified breakpoint (md) and up (lg through xl breakpoints).

The rest of the toolbar’s elements are the opposite: they will be hidden when the device viewport is small (sm) or lower (down).

As for color, Vuetify offers a predefined color palette that you can use via CSS classes.

Lastly, the property @click defines that a click on the hamburger icon will trigger the activation of the navigation drawer; otherwise, the click will not take effect. It is worth remembering that the effect is a toggle.

Body (pricing plans)

Let’s now create our second template: pricing plans. To do this, create a new file named plans.vue in the same directory and add the following content:

<template>
    <v-container grid-list-lg>
        <v-layout column>
            <v-flex class="text-xs-center display-1 my-5">Pricing</v-flex>
            <v-flex class="text-xs-center display-1 font-weight-black">Plans that scale with your user growth</v-flex>
        </v-layout>
        <v-layout row wrap>
            <v-flex>
                <v-card>
                    <v-img :src="images.rocket1" height="300px">
                        <v-container fluid>
                            <v-layout>
                                <v-flex flexbox>
                                    <span class="subheading purple white--text px-1 py-1">DEVELOPER</span>
                                </v-flex>
                                <v-btn small>Try it</v-btn>
                            </v-layout>
                        </v-container>
                    </v-img>

                    <v-card-title primary-title>
                        <div>
                            <h3 class="headline">Developer</h3>
                            <ul>
                                <li>Includes 1,000 sessions / mo</li>
                                <li>14 day data retention</li>
                            </ul>
                        </div>
                    </v-card-title>
                </v-card>
            </v-flex>

            <v-flex>
                <v-card>
                    <v-img :src="images.rocket2" height="300px">
                        <v-container fluid>
                            <v-layout fill-height>
                                <v-flex flexbox>
                                    <span class="subheading purple white--text px-1 py-1">TEAM</span>
                                </v-flex>
                                <v-btn small>Try it</v-btn>
                            </v-layout>
                        </v-container>
                    </v-img>
                    <v-card-title primary-title>
                        <div>
                            <h3 class="headline">Team</h3>
                            <ul>
                                <li>Starting at 10,000 sessions / mo</li>
                                <li>Starting at 1 month retention</li>
                            </ul>
                        </div>
                    </v-card-title>
                </v-card>
            </v-flex>

            <v-flex>
                <v-card>
                    <v-img :src="images.rocket3" height="300px">
                        <v-container fluid>
                            <v-layout fill-height>
                                <v-flex flexbox>
                                    <span class="subheading purple white--text px-1 py-1">PROFESSIONAL</span>
                                </v-flex>
                                <v-btn small>Try it</v-btn>
                            </v-layout>
                        </v-container>
                    </v-img>
                    <v-card-title primary-title>
                        <div>
                            <h3 class="headline">Professional</h3>
                            <ul>
                                <li>Any volume of Sessions</li>
                                <li>Custom data retention</li>
                                <li>On-premise available</li>
                            </ul>
                        </div>
                    </v-card-title>
                </v-card>
            </v-flex>
        </v-layout>
    </v-container>
</template>

<script>
export default {
    name: 'RocketPlans',
     data() {
        return {
            images: {
                rocket1: require('../assets/rocket_1.jpg'),
                rocket2: require('../assets/rocket_2.jpg'),
                rocket3: require('../assets/rocket_3.jpg')
            }
        }
    }
};
</script>

The following is from the Vuetify documentation on the grids system:

The v-container can be used for a center-focused page, or given the fluid prop to extend its full width. v-layout is used for separating sections and contains the v-flex.

The structure of your layout will be as follows: v-container » v-layout » v-flex. Each part of the grid is a flex-box element. The final, v-flex, automatically sets its children to have flex: 1 1 auto.

Our container is a CSS Grid  —  simple and horizontally aligned. The first item in the container is a column: <v-layout>. Here, I quickly aligned two texts in the center of the page with the following props:

  • text-cs-center: align text on the horizontal (x-axis) to the center
  • display-1: Vuetify typography for a default <h4> element size. Refer to the typography documentation for more info
  • my-5: spacing setting for Vuetify margins. Here, m comes from the margin, and y comes from vertical (y-axis). The scale ranges from 0 to 5, according to the Material Design specification
  • font-weight-black: font weight for Vuetify typography. Sets the font weight to 900

The next layout is a row consisting of three cards. Each card (represented by a <v-card>) is composed of two main elements: an image and a title.

An image (<v-img>) is made of components in Vuetify work with src, pointing directly to the image that may be hosted externally. If you want to access the files locally (as in our case), you need to expose each of them as data attributes with their respective paths relative to the assets folder (see the script at the end of the listing). You can access the image files in the GitHub project link at the end of this article.

Within each image, we are creating another container with the prop fluid, which serves to extend the width of the container to the same as the parent container. Each container will contain a span text with the title of each card and a call-to-action button. Note that here we are making use of more props for subheading and alignment (px-1 stands for horizontal padding of value 1 out of 5).

The <v-card-title> component defines the content of the card  —  usually the title and description, but it may contain other elements, such as buttons.

Vuetify will also rearrange the cards vertically when viewed in mobile mode.

Footer

To create the footer, go to the components directory of our project and create the footer.vue file. Add the following content:

<template>
  <v-footer height="auto" dark>
    <v-layout justify-center row wrap>
      <v-flex lighten-2 py-3 text-xs-center white--text>
        &copy;2019 — <strong>LogRocket // Vuetify</strong>
      </v-flex>
    </v-layout>
  </v-footer>
</template>

<script>
  export default {
    name: 'RocketFooter'
  }
</script>

It is a simple footer, composed of items we discussed previously. Note that for each of the .vue files, we need to define the names of each template, i.e., how they will be recognized externally by other

Main page

For the main page, we will use the file already created: App.vue. Add the following content to it:

<template>
    <v-app>
        <rocket-navigation></rocket-navigation>
        <rocket-plans></rocket-plans>
        <rocket-footer></rocket-footer>
    </v-app>
</template>

<script>
import RocketNavigation from '@/components/navigation';
import RocketPlans from '@/components/plans';
import RocketFooter from '@/components/footer';
export default {
    name: 'LogRocketApp',
    components: {
        RocketNavigation,
        RocketPlans,
        RocketFooter
    }
};
</script>

The <v-app>tag is required by Vue. It centralizes the other elements that constitute its application.

The rest of the implementation is relatively simple. All we have to do is import the other component files via the import x from y directive and export them as usual. Note that each tag must be hyphenated before being used in the template. This is how Vuetify recognizes how each camel case has been translated into hyphen-separated.

Conclusion

You can access the full source code from my GitHub repo. If you enjoyed it, please leave a star rating.

Many factors must be considered when starting with a new framework. The advantage of Vuetify is that many of the pros and cons have already been absorbed by Vue itself: all of Vue’s power is in your hands. Period.

In addition, you have to analyze the real needs of your project. As we mentioned before, Vuetify may not suit your project if you rely on the design itself. But if it does, you will have a productive and extremely powerful tool on hand to create amazing web pages.

Have you used Vuetify? If yes, how was the experience? Leave the details in the comments  —  you might help someone who wants to trace the same path.

Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - .

Diogo Souza Brazilian dev. Creator of altaluna.com.br

2 Replies to “Is Vuetify right for you?”

  1. vuetify is great. however, in production we have envcoutnered 2 issues that are problematic.
    1. large data set renderind: vuetify does not support virtualization (said to be added in v3). this makes the component like data-table non usable for us.
    2. no tree table support (not supported in material so maybe its understandable)
    when we chose vuetify some 2 years ago there werent better options that filled these requirement. but today there are… so I think vuetify is lagging behind in this aspect

Leave a Reply