Build a cryptocurrency tracker using Vue.js
Tags: pusher, cryptocurrency, realtime, vue
Published: 2018-05-24 22:04:53

---
title: Build a cryptocurrency tracker using Vue.js
published: true
description: In this tutorial, learn how to build a realtime cryptocurrency tracker using Progressive Web Apps, Vue.js, Node.js and and Service Workers.
tags: pusher, cryptocurrency, realtime, vue
---

> A basic understanding of Vue.js, Node.js and PWA is needed to follow this tutorial.

PWAs (Progressive Web Applications) has already been identified as the future of web applications and the reason is quite obvious. PWAs let you build web apps that are capable of delivering native app-like experiences to users.

From sending push notifications, to caching data for offline retrieval, to background sync, Progressive web apps have got you completely covered. PWAs can also ensure that users are engaged and up to date with dynamic data even with very poor network connections.

> Progressive Web App (PWA) is a term used to denote web applications that use the latest web technologies. Progressive Web Apps, also known as Installable Web Apps or Hybrid Web Apps, are regular web pages or websites but can appear to the user like traditional applications or native mobile applications. The application type attempts to combine features offered by most modern browsers with the benefits of mobile experience. - [Wikipedia](https://en.wikipedia.org/wiki/Progressive_web_app)

This article demonstrates how to build a simple realtime PWA with Vue.js and Pusher. Vue.js is a Progressive Web Framework for JavaScript, it’s easy to use, and requires relatively little code to produce awesome results.

For the realtime part of this application, we will be plugging in Pusher’s JavaScript library. Pusher is a realtime engine that makes it easy to add realtime functionalities to applications.

## What we’ll be building

In this article, we will be building a cryptocurrency application called “KryptoWatcher”. Its function is to display the price updates of three cryptocurrencies (Bitcoin, Ethereum, and Litecoin) in realtime. The price updates will be obtained from the Cryptocompare API.

KryptoWatcher will also be able to travel five days into the past and retrieve coin data for those days. Here’s a visual display of what the final application will look like:

![cryptocurrency-tracker-vue-demo](https://images.contentful.com/1es3ne0caaid/70YNXoNPl6cuAOAmS0SW8a/3b318c61b0a19378604ac5607a3201eb/cryptocurrency-tracker-vue-demo.gif)

The best part of it all is that, after this app runs once, it can run again and display coin data even without an internet connection. This is possible because we’ll build `KryptoWatcher` to cache the coin data on the first run.

Let’s start putting the pieces together.

## Requirements

To follow along in this tutorial, you will need to have the following:

- Knowledge of [Vue.js](https://vuejs.org/).
- [Vue CLI](https://vuejs.org/v2/guide/installation.html#CLI) installed on your machine.
- [Node and NPM](https://docs.npmjs.com/getting-started/installing-node) installed on your machine.
- Knowledge of Node.js and Express framework.
- Knowledge of JavaScript.
- A Pusher Application. [Create one here](https://pusher.com/).

Once you have requirements we can move on to setting up our application.

## Setting up your Pusher application

Create a [Pusher account](https://pusher.com/), if you have not already, and then set up your application as seen in the screenshot below.

![cryptocurrency-tracker-vue-create-app](https://images.contentful.com/1es3ne0caaid/4dQB7DXuQEyecCwE06AuSy/3d775f17a03b1895f67c6e8b80e65e2f/cryptocurrency-tracker-vue-create-app.png)

When you have completed the set up, take note of your Pusher application keys as we will need them later on.

## Setting up our Vue.js PWA application

You can think of the Vue CLI tool as a lightweight tool for scaffolding Vue.js projects. To start building our application we will use the Vue CLI tool to pull in the [Vue PWA template](https://github.com/vuejs-templates/pwa) that we will be working with.

To create our application run the following command on your terminal:

```
$ vue init pwa krypto-watcher
```

You’ll be presented with prompts and a few ‘Yes’ or ‘No’ questions. You can answer most as you see fit, however, for the “Y” or “N” prompts, since we do not require the additional functionalities and features, let’s respond with “N” to all the queries.

![cryptocurrency-tracker-vue-project-details](https://images.contentful.com/1es3ne0caaid/64HwW3fkUouk2OuuGMkmeC/d7b56613d14ecc9f66bb25b55b3e248a/cryptocurrency-tracker-vue-project-details.png)

The template gives us awesome PWA features out of the box. One such feature is the service worker. The service worker allows our application to work offline.

> 💡 A service worker is a script that your browser runs in the background, separate from a web page, opening the door to features that don't need a web page or user interaction.

To install the dependencies, go to your terminal window and run the following command:

```
$ cd krypto-watcher && npm install
```

If you take a look at your project directory, you will find that it contains a few subfolders: `build`, `config`, `src`, `static`, `test`. Let’s open the `build/webpack.prod.conf.js` file and take a quick peek at the `SWPrecacheWebpackPlugin` object:

```
new SWPrecacheWebpackPlugin({ 
	cacheId: 'krypto-watcher', 
    filename: 'service-worker.js', 
    staticFileGlobs: ['dist/**/*.{js,html,css}'], 
    minify: true, stripPrefix: 'dist/' 
})
```

What this does is generate a new service worker when the application is built (with the `npm run build` command).

The service worker will cache all the files that match the glob expression, for offline access, in `staticFileGlobs` which currently points to a non-existent `dist` folder. The `dist` directory will be created when we build our application.

Let’s start building out our application component by component.

### Vue.js components

Similar to other modern JavaScript libraries and frameworks like React, Vue allows us to create components when building applications. Components help us keep our application modular and ensure that apps can be separated into reusable modules.

Let’s build `KryptoWatcher` by creating three reusable components:

1. The `Intro` component which will hold the introductory markup and styles for the application.
2. The `Current` component which will display coin prices in realtime.
3. The `Previous` component which will display coins prices from ‘x days ago’.

Let us start creating the components. We will be doing them manually however you can always use an NPM package like [this](https://www.npmjs.com/package/vue-generate-component) to make it easier to create components. Create a `src/components` directory and create the following files in the directory: `Intro.vue`, `Current.vue`, and `Previous.vue`.

### The intro component

This component has no special functionalities as it just holds the intro markup and styles that will make the app presentable. The HTML goes between the `template` tags and the styles go in the `styles` tag.

In the `Intro.vue` file paste the following:

    <template>
      <header class="hero">
        <div class="bar logo">
          <h3>KryptoWatcher</h3>
          <span class="monitor"><span class="monitorText">receive updates</span></span>
        </div>
        <h1>Realtime PWA that displays updates on cryptocurrencies</h1>
        <h2>Bitcoin, Ethereum, Litecoin?</h2>
      </header>
    </template>
    <script>export default { name: 'app' }</script>

    <style scoped>
    header {
        background: linear-gradient(to bottom right, rgb(0, 193, 131),rgb(50, 72, 95));
        padding: 1em;
        margin-bottom: 1em;
        text-align: center;
        height: 300px;
        color: #fff;
    }
    header h3 {
        color: white;
        font-weight: bold;
        text-transform: uppercase;
        float: left;
    }
    bar { padding: 20px; height: 48px; }
    .monitor{
        text-transform: uppercase;
        float:right;
        background-color: rgba(255, 255, 255, 0.2);
        line-height: 23px;
        border-radius: 25px;
        width: 175px;
        height: 48px;
        margin: auto;
    }
    .monitor:hover, monitorText:hover { cursor:pointer; }
    .monitorText{
        width: 104px;
        height: 23px;
        font-weight: bold;
        line-height: 50px;
        font-size: 14px;
    }
    header h1 { padding-top: 80px; width: 80%; margin: auto; }
    header h2{ padding-top:20px; }
    </style>

That is all for the intro component.

### The current component

In the `Current.vue` component, we’ll write some HTML that displays the prices in realtime as they are updated. Open the file and paste the following inside the file:

    <template>
      <div>
        <h2>Current prices of coins</h2>
        <div id="btc" class="currency">
          <label>1 BTC</label>
          <p>${{currentCurrency.BTC}}</p>
        </div>
        <div id="eth"class="currency">
          <label>1 ETH</label>
          <p>${{currentCurrency.ETH}}</p>
        </div>
        <div id="ltc"class="currency">
          <label>1 LTC</label>
          <p>${{currentCurrency.LTC}}</p>
        </div>
      </div>
    </template>

Below the `template` tags, we will have the `script` tag. This will be where we will handle the scripting of the component. Below the `template` tag in the same file, paste the following code:

```
<script> 
export default { 
	name: 'app', 
    props: { 
    	currentCurrency: { type: Object } 
    }, 
    data () { 
    	return {} 
    } 
}
</script>
```

The script above specifies the `props` the `Current` component should expect. It will be getting it, `currentCurrency`, from the parent component `App.vue`.

Lastly, below the `script` tag, let’s include the `style` for the component. Paste the following code after the `script` tag:

    <style scoped>
    .currency {
      border: 1px solid #F5CE00;
      border-radius: 15px;
      padding: 2em 0em;
      display: inline-block;
      width: 30%;
    }
    div p { font-size: 2rem; }
    h2 { font-size: 1.5em; }
    </style>

That’s all for the `Current` component.

### The previous component

This component should display the prices of coins in the past, five days at most. We’ll also display the dates of each of the days.

Inside the `Previous.vue` file paste the following code:

    <template>
      <div>
        <h2>Previous prices of coins</h2>
        <div id="first">
          <h2>Date:   {{previousCurrency.yesterday.DATE}}</h2>
          <p><label>1 BTC:</label> {{previousCurrency.yesterday.BTC}}</p>
          <p><label>1 ETH:</label> {{previousCurrency.yesterday.ETH}}</p>
          <p><label>1 LTC:</label> {{previousCurrency.yesterday.LTC}}</p>
        </div>
        <div id="second">
          <h2>Date:   {{previousCurrency.twoDays.DATE}}</h2>
          <p><label>1 BTC:</label> {{previousCurrency.twoDays.BTC}}</p>
          <p><label>1 ETH:</label> {{previousCurrency.twoDays.ETH}}</p>
          <p><label>1 LTC:</label> {{previousCurrency.twoDays.LTC}}</p>
        </div>
        <div id="third">
          <h2>Date:   {{previousCurrency.threeDays.DATE}}</h2>
          <p><label>1 BTC:</label> {{previousCurrency.threeDays.BTC}}</p>
          <p><label>1 ETH:</label> {{previousCurrency.threeDays.ETH}}</p>
          <p><label>1 LTC:</label> {{previousCurrency.threeDays.LTC}}</p>
        </div>
        <div id="fourth">
          <h2>Date:   {{previousCurrency.fourDays.DATE}}</h2>
          <p><label>1 BTC:</label> {{previousCurrency.fourDays.BTC}}</p>
          <p><label>1 ETH:</label> {{previousCurrency.fourDays.ETH}}</p>
          <p><label>1 LTC:</label> {{previousCurrency.fourDays.LTC}}</p>
        </div>
        <div id="fifth">
          <h2>Date:   {{previousCurrency.fiveDays.DATE}}</h2>
          <p><label>1 BTC:</label> {{previousCurrency.fiveDays.BTC}}</p>
          <p><label>1 ETH:</label> {{previousCurrency.fiveDays.ETH}}</p>
          <p><label>1 LTC:</label> {{previousCurrency.fiveDays.LTC}}</p>
        </div>
      </div>
    </template>

In the `script` section, we’ll be receiving the `previousCurrency` object from the parent component, `App.vue`. In the same file paste the following code after the `template` tag:

    <script>
    export default {
      name: 'app',
      props: {
        previousCurrency: { type: Object }
      },
      data () {
        return {}
      }
    }
    </script>
    
Lastly, some styles to help things stay pretty:

    <style scoped>
    #first, #second, #third, #fourth, #fifth {
      border: 1px solid #F5CE00;
      padding: 2em 0em;
      max-width: 90%;
      margin: 3px auto;
    }
    #first p, #second p, #third p, #fourth p, #fifth p {
      display: inline-block;
      padding: 0em 1.5em;
      font-size: 1.5rem;
    }
    h2 { font-size: 1.5em; }
    </style>
    
That’s pretty much all the business we have with the three components, they are pretty straightforward. Most of the complexity and app logic are buried in the root component, `App.vue`. Let’s explore that next.

### Setting up the root Componenc

The root component is included by default in every fresh Vue installation in the `src/App.vue` file, so we don’t need to create it. Unlike the other components we created earlier, the root component holds the logic and is more complex than them.

We’ll keep the `template` tag of the root component simple. We include the earlier components, `Intro.vue`, `Current.vue`, and `Previous.vue`, as custom tags and pass in the appropriate `props`.

In the `App.vue` file, replace the contents with the following:

    <template>
      <div>
        <intro></intro>
        <div id="body">
          <div id="current">
            <current v-bind:currentCurrency="currentCurrency"></current>
          </div>
          <div id="previous">
            <previous v-bind:previousCurrency="previousCurrency"></previous>
          </div>
        </div>
      </div>
    </template>

Next, let’s add some `script` and start adding logic in the `script` section. Paste the following below the `template` tag:

    <script>
    import Intro from './components/Intro.vue';
    import Current from './components/Current.vue';
    import Previous from './components/Previous.vue';

    export default {
      name: 'app',
      components: {Intro, Current, Previous},
      data() {
        return {
          currentCurrency: {BTC: '', ETH: '', LTC: ''},
          previousCurrency: {
            yesterday: {}, twoDays: {}, threeDays: {}, fourDays: {}, fiveDays: {}
          }
        }
      },
      methods: {
        // Stub
      },
      created() {
        // Stub
      }
    }
    </script>

The script above does not do much but it sets the stage for our logic. We have set all the defaults for the `data` we will be using in the application and we have defined the `created` method that is called automatically during Vue’s component lifecycle. We also imported the components we will be using in the application.

Before we start adding script logic, let’s add some style for the root component. Below the `script` tag, paste the following code:

    <style>
    @import url('https://fonts.googleapis.com/css?family=Lato');
    * {
      margin : 0px;
      padding : 0px;
      font-family: 'Lato', sans-serif;
    }
    body { height: 100vh; width: 100%; }
    .row { display: flex; flex-wrap: wrap; }
    h1 { font-size: 48px; }
    a { color: #FFFFFF; text-decoration: none; }
    a:hover { color: #FFFFFF; }
    a:visited { color: #000000; }
    .button {
      margin: auto;
      width: 200px;
      height: 60px;
      border: 2px solid #E36F55;
      box-sizing: border-box;
      border-radius: 30px;
    }
    #body {
      max-width: 90%;
      margin: 0 auto;
      padding: 1.5em;
      text-align: center;
      color:rgb(0, 193, 131);
    }
    #current { padding: 2em 0em; }
    #previous { padding: 2em 0em; }
    </style>

### Adding methods to our root component

We need to populate the `method` object with actual methods. We’ll start by defining the methods that will retrieve coin prices for previous days.

### Pulling in dependencies

Since we are getting data from a remote API, we need an HTTP client to pull in the data for us. In this article, we’ll be using the promise based HTTP client `[axios](https://github.com/axios/axios)` to make our HTTP requests. We also need `[moment](https://momentjs.com/)` to easily work with dates.

To add Axios and Moment.js to our project, run the following command in your terminal:

```
$ npm install --save vue-axios axios vue-momentjs moment
```

> 💡 `vue-axios` and `vue-momentjs` are Vue wrappers around the Axios and Moment.js packages.

When the installation is complete, we will globally import the packages to our application. Open the `src/main.js` file and in there replace:

```
import App from './App'
```

with:

    import App from './App'
    import moment from 'moment';
    import VueMomentJS from 'vue-momentjs';
    import axios from 'axios'
    import VueAxios from 'vue-axios'

    Vue.use(VueAxios, axios)
    Vue.use(VueMomentJS, moment);


### Building the methods logic

Next, we want to go back to our root component and build out the `methods` object. In the `methods` object, let’s create the first method. Paste the following code inside the `methods` object in the `App.vue` file:

    _fetchDataFor: (key, daysAgo) => {
      var date = this.$moment().subtract(daysAgo, 'days').unix()
      let fetch = (curr, date) => this.axios.get(`https://min-api.cryptocompare.com/data/pricehistorical?fsym=${curr}&tsyms=USD&ts=${date}`)

      this.axios
          .all([fetch('BTC', date), fetch('ETH', date), fetch('LTC', date)])
          .then(this.axios.spread((BTC, ETH, LTC) => {
              this.previousCurrency[key] = {
                  BTC: BTC.data.BTC.USD,
                  LTC: LTC.data.LTC.USD,
                  ETH: ETH.data.ETH.USD,
                  DATE: this.$moment.unix(date).format("MMMM Do YYYY"),
              }

              localStorage.setItem(`${key}Prices`, JSON.stringify(this.previousCurrency[key]));
          }))
    },

The method above is a helper method for fetching the coin exchange rate within a specified period and saving the response in `localStorage` and the `this.previousCurrency` object. We will use this later in the code.

Next, paste the following function inside the `methods` object alongside the one we added above:

    _fetchDataForToday: () => {
      let url = 'https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,ETH,LTC&tsyms=USD'

      this.axios.get(url).then(res => {
        localStorage.setItem('BTC', this.currentCurrency.BTC = res.data.BTC.USD),
        localStorage.setItem('ETH', this.currentCurrency.ETH = res.data.ETH.USD),
        localStorage.setItem('LTC', this.currentCurrency.LTC = res.data.LTC.USD)
      })
    },

The method above simply fetches the coin data for the current date and saves the response to `localStorage` and the `this.currentCurrency` object.

Next, inside the `created()` method of the root component, paste in the following code:

    if ( ! navigator.onLine) {
      this.currentCurrency = {
        BTC: localStorage.getItem('BTC'),
        ETH: localStorage.getItem('ETH'),
        LTC: localStorage.getItem('LTC'),
      }

      this.previousCurrency = {
        yesterday: JSON.parse(localStorage.getItem('yesterdayPrices')),
        twoDays:   JSON.parse(localStorage.getItem('twoDaysPrices')),
        threeDays: JSON.parse(localStorage.getItem('threeDaysPrices')),
        fourDays:  JSON.parse(localStorage.getItem('fourDaysPrices')),
        fiveDays:  JSON.parse(localStorage.getItem('fiveDaysPrices'))
      }
    } else {
      this._fetchDataFor('yesterday', 1)
      this._fetchDataFor('twoDays', 2)
      this._fetchDataFor('threeDays', 3)
      this._fetchDataFor('fourDays', 4)
      this._fetchDataFor('fiveDays', 5)
      this._fetchDataForToday()
    }

In the code above, we have defined the code to fetch the current currency from `localStorage` if the client is offline. If the client is online though, it fetches the data from the API.

Everything should be working now except the realtime functionality.

![cryptocurrency-tracker-vue-draft](https://images.contentful.com/1es3ne0caaid/70EsAUqmNqSogEYYgwg640/c70439aeb26fd7963309b4cfb39dce7f/cryptocurrency-tracker-vue-draft.png)

### Integrating realtime functionality using Pusher

Now that we have a functional application, we would like to add some realtime functionality so we see updates as they happen.

We will be using Pusher to provide this functionality, if you haven’t, create your Pusher application from the Pusher dashboard as you will need the: `app_id`, `key`, `secret` and `cluster`.

#### Building a Node.js backend for our application

We need a backend server to trigger events to Pusher, we will be using Node.js to build the backend for this article.

To get started, create a new file in the root directory of our application and call it `server.js`. In this `server.js` file, we’ll be using `Express` as the web framework so we need to pull that in. We’ll also pull in `axios`, `Pusher` and `body-parser` since we’d be making references to them in our code.

In your terminal type in the following command:

```
$ npm install --save express axios body-parser pusher
```

When the installation is complete, open the `server.js` file and in the file paste in the following code:

    const express = require('express');
    const path = require('path');
    const bodyParser = require('body-parser');
    const app = express();
    const Pusher = require('pusher');
    const axios = require('axios');


    // Initialise Pusher
    var pusher = new Pusher({
      appId: 'PUSHER_APP_ID',
      key: 'PUSHER_APP_KEY',
      secret: 'PUSHER_APP_SECRET',
      cluster: 'PUSHER_APP_CLUSTER',
      encrypted: true
    });

    // Body parser middleware
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));

    // CORS middleware
    app.use((req, res, next) => {
        res.setHeader('Access-Control-Allow-Origin', '*')
        res.setHeader('Access-Control-Allow-Credentials', true)
        res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
        res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type')
        next()
    });

    // Routes
    app.get('/', _ => res.send('Welcome'));

    // Simulated Cron
    setInterval(_ => {
      let url = 'https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,ETH,LTC&tsyms=USD';

      axios.get(url).then(res => {
        pusher.trigger('price-updates', 'coin-updates', {coin: res.data})
      })
    }, 5000)

    // Start app
    app.listen(8000, () => console.log('App running on port 8000!'));

> 💡 You need to replace `PUSHER_APP_ID`, `PUSHER_APP_KEY`, `PUSHER_APP_SECRET`, and `PUSHER_APP_CLUSTER` with the details from your Pusher application dashboard.

In the Express app above, we import our dependencies and then instantiate Pusher. We then register some middleware including the [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) middleware so we don’t get cross origin request errors.

Next, we have a “Simulated Cron” that runs after 5 seconds. The job is to fetch the updates from the server and send the updates to Pusher. Our Vue application can then subscribe to the Pusher channel, pull the changes and display them.

Finally, we tell the Node app to listen on port **8000**. To start the Node server, run the command below:

```
$ node server.js
```

This will start a Node server and the simulated cron will start running and sending events to Pusher.

## Creating an API proxy

To access our API server from the Vue application, we can create a proxy in `config/index.js` and run the dev server and the API backend side-by-side. All requests to `/api` in our frontend code will be proxied to the backend server.

Open the `config/index.js` and make the following modifications:

    // config/index.js
    module.exports = {
      // ...
      dev: {
        // ...
        proxyTable: {
            '/api': {
            target: 'http://localhost:8000',
            changeOrigin: true,
            pathRewrite: {
              '^/api': ''
            }
          }
        },
        // ...
      }
    }

In the `proxyTable` we attempt to proxy requests from `/api` to `localhost:8000`.

### Using Pusher in our Vue.js application

To use `Pusher` on the client side of our application we need to pull in the `pusher-js`. Run the following command in your terminal:

```
$ npm install --save pusher-js
```

When the installation is complete, we will import `pusher-js` to the root component. Within the `script` tag add the following at the top:

```
import Pusher from 'pusher-js'
```

Next we will initialize Pusher with the app credentials from the Pusher dashboard and subscribe to a channel in the `created()` life cycle hook. Open the `App.vue` and add this to the bottom of the `created()` method in the `else` block:

    let pusher = new Pusher('PUSHER_APP_KEY', {
      cluster: 'PUSHER_APP_CLUSTER',
      encrypted: true
    });

    let channel = pusher.subscribe('price-updates');

    channel.bind('coin-updates', data => {
      this.currentCurrency = {
        BTC: data.coin.BTC.USD, 
        ETH: data.coin.ETH.USD, 
        LTC: data.coin.LTC.USD
      }
    });

In the code above, we subscribe to receive updates on the `price-updates` channel. Then we bind to the `coin-updates` event on the channel. When the event is triggered, we get the data and update the `currentCurrency`.

That’s all now. You can build the application by running the command below:

```
$ npm run dev
```

This should start and open the Vue PWA on your browser. To make sure you receive updates, make sure your Node server is running.

![cryptocurrency-watcher-vue-demo](https://images.contentful.com/1es3ne0caaid/4beBtBCJjqiWK4GKa0Mw0i/13f64db1b24726b8b3e19ccb17aa92a6/cryptocurrency-watcher-vue-demo.gif)

## Using service workers and offline capability

As it is, the application already functions but is not a PWA in true sense of the term. So let us work on making the application a PWA with offline storage. The build process already automatically generates the service worker when the application is built so let’s build the application. Run the following command to build the application:

```
$ npm run build
```

This command creates a `dist` folder in our working directory and also registers a new service worker. Let’s serve this `dist` directory and take a peek at the generated service worker in a Chrome web browser.

We’ll serve this application using an NPM package called [Serve](https://www.npmjs.com/package/serve). Run the following command to install it:

```
$ npm i serve -g
```

When the installation is complete, we will use the package to serve the application. Run the following command to serve the application:

```
$ serve dist
```

We should get an output that looks like this:

![cryptocurrency-tracker-vue-output](https://images.contentful.com/1es3ne0caaid/5D35dFM7PGssemAeMO0IcS/2563dfc1d3b90732117be8babc5ae2d0/cryptocurrency-tracker-vue-output.png)

If we navigate to this address `http://localhost:5000` on our web browser, we’d see our application as it was the last time, no obvious changes except for the fact that the app is now a PWA.

We can inspect this feature by opening the browser’s dev tools and clicking on the “Application” tab. Here’s what we should see:

![cryptocurrency-tracker-vue-service-workers](https://images.contentful.com/1es3ne0caaid/1V3EsE6wwoicCOo6KQgiG0/35da6aa92c35a46419c58dacb08b7a3a/cryptocurrency-tracker-vue-service-workers.png)

Our app registered a service worker that caches the app shell on the first run, thanks to the Vue PWA template.

> 💡 An application shell (or app shell) refers to the local resources that your web app needs to load the skeleton of your user interface (UI). Think of your app's shell like the bundle of code you would publish to a native app store when building a native app.

## Conclusion

In this tutorial, we have seen how to write a simple realtime PWA with Vue.js, Pusher and Service Workers. We also saw how to cache dynamic values from a remote API using the Web Storage API’s storage object. There is a lot more you can do with PWAs and Vue, but this is a good introduction so far.

THis post first appeared on the [Pusher blog](https://pusher.com/tutorials/cryptocurrency-tracker-vue/).