July 21, 2020 By Alex

Building a Real-World Web App With Vue.js and Firebase


Developers are adopting Vue.js at a rapid pace. Boasting over 167,000 stars on GitHub, this modern, modular JavaScript library has become a compelling option for web app development. This adoption is in no small part due to Vue.js being so easy to integrate into an existing project. It's fast. It's extremely powerful. And it allows you to build a dynamic, scalable, maintainable single-page application from scratch.

We just released a major update in July 2020! This release includes the current Vue CLI, async/await instead of promise chaining, and a structure overhaul incorporating updated best practices. The flow remains unchanged with some updates to match the current Firebase SDK. All code for the rewrite can be found in the project's master branch, while the old code remains in original-deprecated for posterity.


  • Complete rewrite of existing project
  • Async/Await instead of promise chaining
  • More components vs. same file functionality
  • Leveraging more actions vs. in-component requests to handle Firebase methods
  • Simplified Firebase integration

Savvy was an early adopter among agencies, using Vue.js to power web apps for Levi's and Pocket Prep. And now we’re leveraging our experience to help developers who are just getting started with Vue.js. Since the best way to learn new concepts is by trying them out in real-world scenarios, we’ve created a sample project that ties together a number of Vue concepts while going beyond a basic to-do app. In this resource you'll not only learn many of the core concepts of working with Vue.js, you'll also get hands-on experience creating a production-ready app.

Why Vue.js?

Vue.js is a JavaScript framework that allows you to easily render dynamic data to the DOM, bind data to DOM elements, and manage/maintain your app's state (local storage) without requiring a user to reload the browser. The growing popularity of Vue.js cannot be understated. We prefer it because it's lightweight, modular, and calls for minimal configuration. It's also extremely fast and has a low file size. Developers can easily drop it into any project or existing framework.

What's more, Vue.js isn't backed by any major company or organization. While that kind of backing comes with its own positives, we feel Vue.js works better as an open-source, third-party framework carrying fewer motives or biases. Normally we would be concerned about the longevity of a product without that type of support. However, we believe Vue is here to stay thanks to its popularity and the activity around it so far.

We won't dive too deep into the benefits of using Vue.js; there are already a number of solid resources that go in-depth on this very topic. Check out Vue.js' own comparison of itself with other frameworks to get a feel for the advantages and differences between the more popular options. Hacker Noon offers a roundup of opinions about Vue.js versus other platforms from prominent developers. You'll also want to stay up to date on Vue.js features as well as its development roadmap on the Vue news site.

What We Will Be Making - Vuegram

We created Vuegram, a simple social media web app, to help you get hands-on experience with the core features of Vue.js. Vuegram allows users to create an account, log in, publish posts, and interact with other users by adding comments and “liking” posts all in realtime. Although there are multiple ways to create an app like Vuegram, the code in this tutorial illustrates some of the common patterns found in production-ready apps made with Vue.js.

In constructing Vuegram, you'll learn how to:

  • Start a production-ready project using Vue CLI.
  • Manage state for your app using Vuex. You'll also understand why a central store is important for building large-scale apps.
  • Maintain reactivity within your app by utilizing built-in Vuex features.
  • Authenticate routes with vue-router.
  • Read/write to a database using Firebase's Cloud Firestore.
  • Set up authentication using Firebase.
  • Leverage components, one of the more powerful features of Vue.js.
  • Familiarize yourself with core Vue.js concepts.

These concepts and patterns will enable you to get up and running with Vue.js, serving as a foundation to build more complex and scalable web apps. They'll give you a greater sense of how to scale your Vue app and manage a lot of files easily. You'll also be positioned to prevent pitfalls before they happen, potentially saving loads of development time.

Head over to GitHub to access the Vuegram project and use it to follow along with the rest of this tutorial. Now let's get started!

Initial Setup With Vue CLI

For your reference, please see the Vue CLI documentation. Follow these steps to install and initialize via Vue CLI 3:

Step 1: Install Vue CLI 3.

<code class=" language-javascript">npm install <span class="token operator">-</span>g @vue<span class="token operator">/</span>cli</code>

Step 2: Initialize your project with Vue CLI 3.

<code class=" language-javascript">vue create vue<span class="token operator">-</span>app</code>

Step 3: Configure Vue CLI 3.

Choose "Manually select features." Then select "babel," "Router," "Vuex," "CSS Pre-processors," and "linter/formatter." You will be prompted to select your preprocessor. Choose whichever you like; we use sass via node instead of dart.

Since we will be using the default linter for error prevention only, you might want to add a rule to your .eslintrc.js file that turns off a common linter error ('value' is defined but never used).

// the code - add the 'vue/no-unused-components' rule
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'vue/no-unused-components': 'off'

Step 4: Serve up a localhost.

Once everything is installed, navigate to the root folder and run `npm run serve` in the terminal to serve up a localhost.

File Structure Overview

The current version of the Vue CLI sets up your project in a very specific way that allows for scalable and familiar file structure across projects. The first thing we will want to do is create all of our views within the views folder: Login.vueDashboard.vue, and Settings.vue. The basic structure of a Vue single file component includes your markup, script, and style tags.



export default {


<style lang="scss" scoped>


Next we want to set up our router so these views are available when we visit those routes. The current version of the Vue CLI leverages code splitting, which is a great way to reduce initial load times for your project; the code is only served up when it’s needed.

import Vue from 'vue'
import VueRouter from 'vue-router'
import Dashboard from '../views/Dashboard.vue'


const routes = [
path: '/',
name: 'Dashboard',
component: Dashboard
path: '/login',
name: 'Login',
component: () => import(/* webpackChunkName: "login" */ '../views/Login.vue')
path: '/settings',
name: 'settings',
component: () => import(/* webpackChunkName: "settings" */ '../views/Settings.vue')

const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,

export default router

Let’s clean up the project a bit by removing starter boilerplate files.

  • Home.vue and About.vue in the /views folder
  • assets/logo.png
  • components/HelloWorld.vue

Clean up project App.vue by removing the starter code, otherwise our app will throw errors trying to render components that don’t exist.

<div id="app">

State Management With Vuex (And Props)

Deciding how to handle state management is critical when starting a project with Vue.js. One solution, Vuex, is a pattern and library that handles state management for Vue.js. It provides the ability to share data across all components from a single place.

You could also create your own system for handling state management using props. Props are part of Vue.js; they pass data to a child component. For example, say you have a settings view. In that settings view you have a modal that functions as its own component because you use it in multiple places. In this case, you need to pass data from the settings view (parent) to the modal (child). You would use props to pass this data.

Depending on your project size and complexity, using Vuex to create a central store for all of your reactive data makes it easier to pass data to props regardless of their hierarchy. That's because a child component speaks directly to the Vuex store object. If you decided to accomplish this using props alone, you would need to pass data down the chain instead of directly to the component. Likewise, to pass data from a child component up two or more levels, you would need to pass that data up the chain instead of passing directly to the store. In that case, your parent component would then have those updates.

You may be tempted to forgo Vuex entirely and rely on props when you just need to pass data to a child component (example: modals, alerts, etc.). We don't recommend it, though, as it is bad practice to pollute the store with this kind of simple downstream data flow. We prefer Vuex because it provides an easy and predictable pattern to use when updating parts of an app. Vuex keeps data reliable as it provides a single point of truth, whereas with props, you can have one component with a user property that's been changed and another component that remains unchanged. Keeping track of different versions of the same component can easily lead to chaos.

With that context, it should be no surprise that we'll use Vuex for managing the entirety of this app's local state.

Setting Up a Database With Firebase Cloud Firestore

We're big fans of FirebaseSavvy uses Firebase as one of its backends for web and native apps, as well as for real-time updates, user authentication, static hosting, push notifications, and more. In this section you'll learn how to use one of our favorite Firebase tools, Cloud Firestore, to set up a back-end database and integrate it within a Vue.js project.

Cloud Firestore is one of two Firebase tools that allows developers to set up and provision a back-end database as a service. Released in 2017, Cloud Firestore provides a different architecture and focuses on different priorities than the older Firebase offering, Realtime Database. It's important to choose the best database option for your project before beginning. We go in-depth on the benefits of using Cloud Firestore and compare it to Firebase's other backend offering, Realtime Database, in Choosing a Firebase Database for Your App.

Note that while Realtime Database would be more cost efficient in a real production app similar to the Vuegram project, we chose to integrate Cloud Firestore for the backend as practice in setting up the new service in a new Vue.js project.

For this part of the tutorial, follow along with the Firebase Console and the firebase.js file in the Vuegram GitHub repo.

Step 1: Create your Firebase project.

To create a Firebase project, go to the Firebase Console and click “add project.” Give it a name, then click “create project.” Click “Add Firebase to your web app.” Be sure to copy this code. You will need it later when you set up the firebase.js file.

Click “Authentication” in the left-hand navigation, then “Set sign-up method,” then “email/password.” Enable and save.

Select “Database.” Click “Get started” for Cloud Firestore (currently in beta). Start in test mode and enable so anyone can read/write to the database. You will add basic security rules later.

Step 2: Install package.

Open your command line and install Firebase.

<code class=" language-javascript">npm i firebase</code>

Step 3: Create the firebase.js file.

Import Firebase and Cloud Firestore, and set up Firebase configuration. The code you copied when you created your project will replace the current configuration code. Create your basic utility methods/variables, then update the firebase.js file with your own Firebase credentials.

Since Cloud Firestore is in beta, changes will happen from time to time.

Create your references to the following collections: users, posts, comments, and likes. Now, export!

import * as firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/firestore'

// firebase init - add your own config here
const firebaseConfig = {
apiKey: '',
authDomain: '',
databaseURL: '',
projectId: '',
storageBucket: '',
messagingSenderId: '',
appId: ''

// utils
const db = firebase.firestore()
const auth = firebase.auth()

// collection references
const usersCollection = db.collection('users')
const postsCollection = db.collection('posts')
const commentsCollection = db.collection('comments')
const likesCollection = db.collection('likes')

// export utils/refs
export {

Step 4: Update the main.js file to handle authentication state changes (like the page reload and login).

Import your Firebase configuration and set up the Firebase method onAuthStateChanged. This ensures Firebase initializes before loading the app when a user refreshes a page.

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import { auth } from './firebase'

Vue.config.productionTip = false

let app
auth.onAuthStateChanged(() => {
if (!app) {
app = new Vue({
render: h => h(App)

With this complete, you're ready to tackle authentication by setting up vue-router.

Authenticating Routes With vue-router

The next step is learning what vue-router is, how to use it to authenticate different routes within your app, and how to set it up.

The officially supported vue-router let’s you define how users interact and access the different views in your app. Since Vue.js can create single-page applications, there's no need to reload your app when switching between views. This is handled by telling Vue.js which view to show and when to show it. For example, in Vuegram you'll want to lock down all views besides the login screen if a user is not authenticated.

For this part of the tutorial, follow along with the index.js file in the Vuegram GitHub repo.

Step 1: Import Firebase auth.

import Vue from 'vue'
import VueRouter from 'vue-router'
import Dashboard from '../views/Dashboard.vue'
import { auth } from '../firebase'

Step 2: Add a meta object to routes we want to lock down.

This allows us to flag the routes required for a user to be authenticated. We do this by adding an object named meta with a property requiresAuth: true

const routes = [
path: '/',
name: 'Dashboard',
component: Dashboard,
meta: {
requiresAuth: true
path: '/login',
name: 'Login',
component: (

About Author

Related Posts

Subscribe our newsletter to get
latest news & updates

Lorem ipsum dolor sit amet consectetur adipiscing elit