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

Is Vuetify right for you?

11 min read 3106

The advent of new frameworks for visual component encapsulation in web apps has highlighted a number of 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 the frameworks that have adopted the famous Material Design standard. The community has widely accepted this option since it embraces both the desktop and mobile universes with responsiveness, flexibility, and extensibility.

Since 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 is born.

Why use Vuetify?

Vuetify complies with the Material Design specification. This means 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 totally 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

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.

To install the Vue CLI in a global way, it is important to note the desired version. As of the writing of this article, the latest version is 3.x.x. Many sources point out that the command for this is:

npm install -g vue-cli

However, this command will, in fact, install the previous version of the Vue CLI, so be aware of that.

Also make sure to check whether you already have any previous version installed on your machine:

vue — version

To install the correct version, run the following command:

npm install -g @vue/cli

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:

Picking up 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 basic structure of directories and files generated after adding the plugin:

Created directories and files

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 our 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, since 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 X 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>

<style scoped>
</style>

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 base 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>

<style scoped>
</style>

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: <v-img> 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)
  • A title: 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>

<style scoped>
</style>

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 .vue files.

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>

<style>
</style>

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.js is that many of the pros and cons have already been absorbed by Vue.js 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 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. 🙂

Plug: LogRocket, a DVR for web apps

https://logrocket.com/signup/

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

Try it for free.

 

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

Leave a Reply