Building a Singe Page App with Vue.js Tutorial

In this post we’ll create a single page app with Vue.js that shows a random quote or string. We’ll start from scratch and use the default webpack-simple boilerplate provided by the creators of vue through vue-cli.

This again is part of the Making of post series, where I walk you through all parts that were necessary to create the page.

For this tutorial you’ll need to have node.js including npm installed.

My video course on Vue.js has just been released with PACKT:

Click here to check it out!

Creating a Vue.js Project

We’ll use the webpack-simple vue template and basically follow the instructions in a terminal:

npm install -g vue-cli
vue init webpack-simple vue-example-spa
cd vue-example-spa
npm install
npm run dev

This will create a boilerplate webpack project with Vue.js included. Once you have run npm run dev it should open a browser tab pointed at localhost:8080 and you now should see this:

Next, let’s have a look at the files that have been created for us:

tree -I node_modules
├── index.html
├── package.json
├── src
│   ├── App.vue
│   ├── assets
│   │   └── logo.png
│   └── main.js
└── webpack.config.js

The most important for us are src/App.vue and src/main.js for now. package.json holds our dependencies and webpack.config.js has the tasks for development process and the build task which we’re going to get back to.

Let’s go ahead and change the <template> portion of App.vue until it looks like this:

  <div id="app">

Building your Frontend Logic

If you look at, the displayed excuses changes when you either click the quote or the refresh button. Let’s first add a couple of quotes to our App.vue file to mimic that functionality:

  <div id="app">
      — <cite>{{}}</cite>
export default {
  name: 'app',
  data () {
    return {
      currentQuote: {},
      quotes: [
          content: "Overcoming fear and conceiving this 'art of more' should be a fundamental practice in what it is that you do and make.",
          author: "Chase Jarvis"
          content: "As everything in this world is but a sham, Death is the only sincerity",
          author: "Yamamoto Tsunetomo"
          content: "The world needs actual excitement and emotion more than it needs cool people.",
          author: "Amanda Palmer"
  created: function() {
  methods: {
    displayQuote: function(){
      var randomInt = Math.floor(Math.random() * (this.quotes.length - 0));
      this.currentQuote = this.quotes[randomInt];

Let’s focus on the <script> part. We have changed the return object of the data function and now it contains an empty currentQuote and some other quotes by some cool people. The created function runs when the component is displayed. Inside methods we have defined a function that grabs one of the quotes at random called displayQuote. The random quote is then set as the value of currentQuote of the data function.

Try out this example and hit CTRL+R or CMD+R repeatedly, you should see the quotes changing. See the following screenshot for reference:

Now it’s not exactly what we want, because we don’t want anyone to hit their refresh button in order to skip through the quotes.

Click Events with Vue.js

Defining a click event with Vue is rather easy, you can simply bind the click event to a function of the same component like this, only changing the template:




<blockquote v-on:click="displayQuote()">

This will cause the function that randomly picks a quote to do that every time you click the quote or quote author. Keep in mind that the function name must be identical to the one used inside the methods object of your component.

Making XHR / AJAX requests with Vue and Axios

If you, like me, would like to to retrieve data from an API instead of an array that’s already in your JavaScript code, you have a number of options, a fairly popular one is [axios][], but you can also use jQuery or other libraries that you may be using already, it’s really not important, just get the response and change the state of your data in your Vue component.

If you want to know more about creating APIs, we’ve used Go and I blogged about it here: Building a Go(lang) API with echo and MySQL. The API in that example emits a random excuse whenever the default route is hit.

First, let’s install axios with npm:

npm install axios --save

To use it in our code, we need to import it. This JavaScript dependency model is not currently supported by browsers, but luckily webpack will take care of that for us and convert the code.

import axios from 'axios';

In this example let’s use the fantastic httpbin example API to make a request and retrieve some data every time we refresh the quote. First, let’s add the new data we want to retrieve as keys on our data object, in this instance: userAgent.

  data () {
    return {
      userAgent: '',
      currentQuote: {},

Next we need to change the displayQuote function to actually do an HTTP request. To preserve the context of the vue component we will assign self to this on top of the function, so we have a reference to it inside the promise resolve function of the axios request:

displayQuote: function(){
    var self = this;
    var randomInt = Math.floor(Math.random() * (this.quotes.length - 0));
    this.currentQuote = this.quotes[randomInt];

    .then(response => {
      self.userAgent =['user-agent'];
    .catch(error => {

The first .then block is run if the request is successful, the second if the request fails for any reason.

Finally we’ll change the template to display the user agent by adding:

<p>Your user agent: {{userAgent}}</p>

into the <div id="app">. Now your browser should look like this:

Deploying your Single Page App

To deploy your app, you just need to run npm run build and copy index.html and the dist folder to a webspace of your choosing. If you want to deploy it to a VPS you can read my most recent VPS comparison.

Refining Next Steps

I have implemented a bit more functionality, for example we can link to specific excuses for photographers based on the URL, so actually gets the excuse with id 67.

For that we’re registering the following route in our vue-router:

{ path: '/id/:id', component: App }

and extracting that id with:


We’re also watching for changes or the in order to support the browser back button. On top of that we’re utilizing marked to render markdown, like for this excuse (the AV is in bold).


Building Single Page Apps with Vue.js is fairly straight forward, there is not a lot to keep in mind, there is tons of tutorials and the framework makes it very easy to do simple things. You really don’t need half a day to set up a project, the barrier of entry is low.

If you’d like to know more about Vue or would like me to write about other things, let me know! If you liked the post, consider subscribing to me on twitter or feedly, if I forgot anything, please let me know in the comments!

Thank you for reading! If you have any comments, additions or questions, please leave them in the form below! You can also tweet them at me

If you want to read more like this, follow me on feedly or other rss readers

5 thoughts on “Building a Singe Page App with Vue.js Tutorial”

  1. Hi man! Great post, the tut was easy to follow and I liked how you broke it up into separate parts without over complicating it, thanks keep it up!

  2. Have you figured out stateful routing in Vue. I have a scenario with multiple filters on a table that I would like to be routable, meaning that when I select values for the filters the route will change accordingly, and if I navigate to the route the filters state will reflect the params in the route.

    1. Sounds like you have multiple and optional route parameters that you need to watch and give to your child-components or state system.

      My way of trying to realise this would probably be to make up to multiple attempts and see what works best, since there are definitely multiple solutions to this. You don’t need to change route/component when the route changes, but you can actually rewrite the history as you please (almost ;))

Leave a Reply

Your email address will not be published. Required fields are marked *