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 fits 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 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. Also I don’t want “a-la-carte components” and a custom theme.

Open a project folder, install Webpack and start a development server (I will use NPM):

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.

Basic layout

Now, source code for this app you can find under /src. In this folder you can see two files and several folders. File main.js is a main entry point of your 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.

Our initial 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 of Vuetify. Inside v-app we have a three components.

Component v-navigation-drawer is a sidebar menu which will appear on a small screen (width less than 576px). 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. Inside toolbar there is v-toolbar-side-icon which represents a burger icon. This icon you can see only on devices with small screens (because of class hide-sm-and-up). This icon opens a sidebar by listening of click event (@click = "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-content is a wrapper for Vuetify grid. Here we will render our main content.

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

In <script> section I only declared two data variables.


Great! Now we have a 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 HelloWorld.vue you can delete later). The purpose of those views is simple. Landing will be generated on a 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. Just copy this code to others, 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-content tag add:


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 localhost:8080/home in address bar 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:

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' }


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:

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

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 v-model="sidebar">
v-for="item in menuItems"
<v-icon>{{ item.icon }}</v-icon>
<v-list-tile-content>{{ item.title }}</v-list-tile-content>

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-for="item in menuItems"
<v-icon left dark>{{ item.icon }}</v-icon>
{{ item.title }}

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

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 index.js file. Open index.js and put there the following code:

This code is pretty straightforward. First we import Vue and Vuex, after that we tell Vue to use Vuex, and finally export our store. About 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.

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

Basically, we need to import our store (in the top). And then to pass it to our Vue instance (line 16).

Done! The store is ready to use. Let’s test it. Add a new variable to state (file /store/index.js):

You can use it in our App.vue like this:

Here I first commented line with existing data property appTitle and added computed property with corresponding name. It just return a state 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:


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:

If you like this tutorial you can donate me a few bucks💰 to my paypal account.

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

Random guy on the internet