Basic Single Page application using Vue.js and Firebase — Part 1

In this tutorial I want to describe how to build a basic scaffold application with Vue.js and Firebase which includes:

  1. Authentication
  2. Real-time database
  3. Nice user interface based on material design

From technical point of view I will use the following:

  1. Vue.js — modern JavaScript framework
  2. Vue-router — for routing
  3. Vuex — for state management
  4. Firebase — for authentication, real-time database and hosting
  5. Vuetify — nice framework to create material design UI

This tutorial is not for complete beginners. The main prerequisite is knowledge of ES6 and at least basic knowledge of Vue.js. Having said that, any experience with front-end frameworks like React or Angular probably be sufficient. So let’s get started!


Setting up a project

First, install the official Vue.js command line interface (CLI) as follows:

$ npm install --global vue-cli

Now we are ready to start the project. For this app we will use Vuetify — a set of material design components compiled into framework. It helps to quickly create user interfaces and perfectly fit with Vue.js. Vuetify has a several predefined templates integrated with vue-cli, which you can use instead of default Vue.js starter project. To initialize new Vue/Vuetify project run:

$ vue init vuetifyjs/webpack-advanced demo-app

This command will download a Vuetify template and setup webpack for you. During setup you’ll need to answer on application related questions. Firstly, choose your app name, project description, author (or just press Enter if you want the default values). Next few options are important for us. Answer “Runtime-only” for Vue build, “Yes” for installing vue-router, “Standard” ESLint preset, “No” for both tests.

Open project folder, install node and start a development server:

$ cd demo-app
$ npm install
$ npm run dev

After npm finish installation of all dependencies (about minute or so) you will see development server up and running at localhost:8080:

Cool. We have working development server.

Note: If command npm run dev fails to run, you can try to delete folder with application and start again from the beginning. But second time you should try to answer “Yes” to add tests.

Basic layout

Now, source code for this app you can find under /src. In this folder you can see two files and several other folders. File main.js is a main entry point of you app, App.vue is a single file component attached to your application. Under /assets folder you can store static assets, /components contains other single file components, /router is for routing, /stylus contains styles for Vuetify in .styl format (more on CSS generator Stylus here).

Our homepage looks fine. You can definitely use this code as a starting point. Instead I want to start from scratch. For now I want to have only toolbar at the top and a navigation sidebar which appears only on small devices. Here is the code of App.vue:

Let’s see what we have here.

In <template> section I first added v-app tag because it’s a requirement by Vuetify. Inside v-app we have three components.

Component v-navigation-drawer is a sidebar menu which appears on small screens (width less than 576px). Attribute temporary means that sidebar will float above the page content. Sidebar is controlled by v-model="sidebar", we change the value of sidebar by clicking on toolbar’s side icon.

Component v-toolbar represents top bar. Attribute fixed="true" means that it will stick to the top of the page. Inside toolbar there is v-toolbar-side-icon which represents a burger icon. This icon you can see only on devices with screen (because of class hide-sm-and-up). This icon opens a sidebar on small devices by listening click event (@click.stop = "sidebar != sidebar). v-toolbar-title represents app title, v-spacer is a spacer element which will help us later when we introduce addition menu items.

Finally, v-container is a wrapper for Vuetify grid. It should be inside main tag. Here we will render all our main content.

Those and many other components of Vuetify framework you can find in their documentation. Highly recommend you to look through it.

In <script> section I only declared two variables. <style> section imports file with styles.

Routing

Great! Now we have scaffold for our app. Let’s add routing.

We already have the router installed in our app in /router/index.js. It is not connected with routes though. So now it’s a good idea to create a few views and then connect them to router.

Under /components folder create several files with names Landing.vue , Signin.vue , Signup.vue and Home.vue (existing file Hello.vueyou can delete). The purpose of those views is simple. Landing will be generated on root path /, Home will be a view for an authorized users. Signup and Signin speaks for themselves. For now let’s keep these views simple and skip any functionality behind them. Here is a code for Landing.vue:

Three other files will be pretty much the same, content we will add later. Now let’s configure our router. Open /router/index.js and change code to:

Here I use a technique for routes called lazy loading. Basically, you need to create an array of router views (in my case routerOptions) and then use map function over it to create routes. Then just pass them to router configuration object. And last thing is that I configured router to work in HTML5 history mode.

Another small but important code you need to add is in file App.vue. Inside v-container tag add:

<router-view></router-view>

This tag will tell Vue-router to render your routes inside it. If all code is correct you should see text Landing page text instead of empty space. Next you can try to type in address localhost:8080/home and you should see a text that you entered on corresponding Home.vue template. This will mean that router is configured properly.

Adding menu

Typing localhost:8080/home every time you want to change the page isn’t a case. Let’s add a menu.

All changes will be done in App.vue, where our toolbar is. First add a new data property menuItems:

<script>
export default {
data () {
return {
appTitle: 'Awesome App',
sidebar: false,
menuItems: [
{ title: 'Home', path: '/home', icon: 'home' },
{ title: 'Sign Up', path: '/signup', icon: 'face' },
{ title: 'Sign In', path: '/signin', icon: 'lock_open' }
]
}
}
}
</script>

Each item has a title, corresponding path and icon which we will use near the title (list of available icons is available here, by default Vuetify uses the material design iconset). I omitted root / path in menuItems because I want that my app title in toolbar lead me to this path. So let’s change it a bit:

<v-toolbar-title>
<router-link to="/" tag="span" style="cursor: pointer">
{{ appTitle }}
</router-link>
</v-toolbar-title>

Now our appTitle is wrapped by router link. It points to /, option tag="span" will not change appearance of the title. cursor: pointer style will change the cursor to pointer when you hover it over title.

It’s time to show our menu on the page. Add following code to navigation drawer:

<v-navigation-drawer temporary v-model="sidebar">
<v-list>
<v-list-tile
v-for="item in menuItems"
:key="item.title"
:to="item.path">
<v-list-tile-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-tile-action>
<v-list-tile-content>{{ item.title }}</v-list-tile-content>
</v-list-tile>
</v-list>
</v-navigation-drawer>

Here I use v-for directive for loop through my menuItems and render them to a list. Each list tile has a unique key attribute (I bind item.title to it) because Vue.js requires it. Then I bind item.path to router attribute to in order this list item to be a link. Finally I populate icon and title to corresponding tags.

Very similar code is for toolbar. Place following code right after v-spacer tag:

<v-toolbar-items class="hidden-xs-only">
<v-btn
flat
v-for="item in menuItems"
:key="item.title"
:to="item.path">
<v-icon left dark>{{ item.icon }}</v-icon>
{{ item.title }}
</v-btn>
</v-toolbar-items>

Instead of list here I use Vuetify buttons v-btn. The whole v-toolbar-items will be hidden on xs width devices.

Note: in Vuetify 0.15 you can remove dark attribute for v-icon to display icon in dark colour

Final code of App.vue:

Check the menu now. You should be able to navigate to any path by clicking on links.

Adding content to pages

For now our pages don’t have any content except the pages name. Let’s fix this quickly.

First I will populate Landing.vue that it looks more like real landing page:

Next will be Signup.vue page:

The structure is simple. Here we have a three text fields with email, password and password confirmation. I omitted the functionality so far, will add it later.

Sign in page looks very similar to sign up. Here is code for Signin.vue:

And finally home page. Very simple. Home.vue:

Now with content it’s easier to define on which page you are.

State management with Vuex

Another big part of this application is a state management system. We will use Vuex for this purpose. I like this description of Vuex from official documentation:

It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion

Let’s install it first. Stop the current dev server if it still running with ctrl+c. Run:

$ npm install --save vuex
# restart dev server again
$ npm run dev

Now we need to create a store. In /src create a new folder /store and several files inside it: index.js, state.js, mutations.js, actions.js, getters.js. Open index.js and put there the following code:

This code is pretty straightforward. First we import Vue and Vuex, next we import our store parts. After that we tell Vue to use Vuex and finally export our store. Now on store components.

state is an object with application data. mutations are needed to change that state. actions are needed to dispatch mutations. And getters are needed to get the store. For now all of them will just export an empty object. For example here is the state.js:

Mutations, actions and getters have pretty much the same code, except of the name of constant they are exported.

Finally, to use this store in our app we need to import it. Let’s add our store to main.js:

Done! The store is ready to use. Let’s test it. In state.js add a new variable:

In getters.js we need to define getter function like this:

And finally let’s use it in our App.vue:

Here I first commented line with existing data property appTitle and added computed property with corresponding name. It just return a getter appTitle from object $store which is available globally in the app (thus we can access it by calling this.$store). Now you should see “My Awesome App” title instead of just “Awesome App” in toolbar.

If you completed all the steps above you should get something like this:

Summary

Let’s stop here and wrap up what we have.

At this point we have very basic structure of our SPA. It has only four views but it’s enough to create a basic sign in and sign up functionality. Also following best practices we have central state management system. Now we are fully ready to proceed with adding Firebase and implementing authorization functionality. This will be covered in part 2.

Code for this part is available here:

https://github.com/oleg-agapov/basic-spa-vue-firebase/tree/part-1

Cheers,

Oleg

Special thanks to Peter Trotman who helped me with editing this article.