Progressive Web App Tutorial: from existing website to PWA

By , last updated November 3, 2019

Progressive Web Apps or PWA are on the rise and we wondered if we could easily turn our website into a PWA or build a native app.

This tutorial will show simple steps to migrate an existing WordPress website to a Progressive Web App. We will show a simple PWA builder tutorial with an example of our own site studiofreya.com.

The product of our website is mostly content produced by us, so we didn’t really need an app. It turns out it is very easy to convert an existing website to a Progressive Web App in just a few simple steps.

How do you see if a website is a Progressive Web App?

It is not easy to see if the website is a PWA or not right now.

On desktop:

  1. Download the latest version of Chrome
  2. Open a good PWA example site, for example Flappy bird game
  3. Click on the 3 dots in the upper right corner of the browser
  4. You will be able to install Progressive Web Flap on your desktop

If you can’t see the menu item “Install …” than the website is not a Progressive Web App.

Find out why your website is not a PWA

A PWA is just another website that can be downloaded to your Homescreen from a web browser. In order for the web browser to know whether or not your website can be downloaded it should satisfy a couple of criteria.

If your website satisfies the criteria, you have a PWA.

If not… find out what is missing!

Here’s how to see what criteria your website satisfies and what not:

Step 1 Download latest Chrome Browser (v.70 as of right now).
Step 2 Go to your website.
Step 3 Go to developer tools by clicking F12.
Step 4 Go to “Audits” and click “Run audits”. Lighthouse plugin will start to check your website for different metrics.
Step 5 When the report is finished scroll down to “Progressive Web App” section and see all the things you need to fix in order for your website to become a PWA.

In the case of studiofreya.com we didn’t pass on 6 aspects of a Progressive Web App.

Let’s fix them!

Create a manifest file

Manifest is a file that will tell browsers about your website as an application. It will tell browsers that here we have a Progressive Web App and how it should behave when installed on user devices.

Here’s how to create a Manifest file for your website in 3 simple steps:

Step 1 Google “website manifest creator”. We found an easy good website manifest creator https://app-manifest.firebaseapp.com/
Step 2 Fill inn the data:
App Name: Studiofreya
Short Name: Sf
Theme Color: white
Background Color: white
Display Mode: Standalone
Icon: upload your website icon

Click “Generate zip”
Step 3 Unzip the manifest file and icon images into your website root folder. If you have a WordPress website, it should go under htdocs-folder.
Step 4 Provide link to the manifest file in your pages :

<link rel="manifest" href="/manifest.json" />

Now you should be able to see your website’s Manifest file under the “Application” tab in Chrome:

Here’s how our resulting manifest.json file looks like:

{
  "name": "Studiofreya",
  "short_name": "Sf",
  "theme_color": "#ffffff",
  "background_color": "#ffffff",
  "display": "standalone",
  "Scope": "/",
  "start_url": "/",
  "icons": [//all the icons],
  "splash_pages": null
}

Create a service worker

Service worker is a JavaScript file that tells the browser how your website will function as an app and offline.

Step 1 Register service worker in your index.html:

<script>
    if('serviceWorker' in navigator) {
        navigator.serviceWorker.register("/sw.js");        
    }
</script>

Step 2 Create Service Worker file

There are several ways to create a service worker file for your website. The easiest are either to create a simple empty service worker or use a tool like https://www.pwabuilder.com/ to help with the task.

Simple service worker from scratch

The first thing your simple service worker should do is to cache your CSS files. Here’s a basic add to cache JS code that we will use for Studiofreya:

const urls = ["/", "/styles.css"];

self.addEventListener("install", event => {
    console.log("The SF is now installed"); 
    event.waitUntil(caches.open("myAppCache")
       .then(function(cache) {
           return cache.addAll(urls);
       })
    );
});

Next thing our service worker will do is to fetch either cache first or network first:

Fetch: Cache First

self.addEventListener("fetch", event => {
    event.respondWith(caches.match(event.request)
        .then(function(response) {
            if (response) {
                // The request is in the cache 
                return response;
            } else {
                // We need to go to the network  
                return fetch(event.request);
            }
        })
    );
});

Fetch: Network first (server or connection errors)

self.addEventListener('fetch', event => {
    event.respondWith(
      fetch(event.request).catch(function(error) {
        return caches.open(myAppCache).then(function(cache) {
          return cache.match(request);
        });
      })
    );
  });

Autobuilt service worker

We used an online pwabuilder to build a service worker for our website. Here’s what it came up with:

//This is the "Offline copy of pages" service worker

//Install stage sets up the index page (home page) in the cache and opens a new cache
self.addEventListener('install', function(event) {
  var indexPage = new Request('index.html');
  event.waitUntil(
    fetch(indexPage).then(function(response) {
      return caches.open('pwabuilder-offline').then(function(cache) {
        console.log('[PWA Builder] Cached index page during Install'+ response.url);
        return cache.put(indexPage, response);
      });
  }));
});

//If any fetch fails, it will look for the request in the cache and serve it from there first
self.addEventListener('fetch', function(event) {
  var updateCache = function(request){
    return caches.open('pwabuilder-offline').then(function (cache) {
      return fetch(request).then(function (response) {
        console.log('[PWA Builder] add page to offline'+response.url)
        return cache.put(request, response);
      });
    });
  };

  event.waitUntil(updateCache(event.request));

  event.respondWith(
    fetch(event.request).catch(function(error) {
      console.log( '[PWA Builder] Network request Failed. Serving content from cache: ' + error );

      //Check to see if you have it in the cache
      //Return response
      //If not in the cache, then return error page
      return caches.open('pwabuilder-offline').then(function (cache) {
        return cache.match(event.request).then(function (matching) {
          var report =  !matching || matching.status == 404?Promise.reject('no-match'): matching;
          return report
        });
      });
    })
  );
})

This code threw an exception in runtime:
Uncaught (in promise) TypeError: Failed to execute 'fetch' on 'ServiceWorkerGlobalScope':
'only-if-cached' can be set only with 'same-origin' mode at sw.js

The error is fixed by adding the following code for request cache check as the first line in fetch method:

if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin')
return

No matter how you built your service worker, store the file on your server and you are done!

Now we were able to pass the Audit and our app in now installable from the Chrome menu:

Other errors

Here are some other errors you may encounter while converting your website into a progressive Web App:

No meta name= theme-color tag found

This is a color that will be applied automatically to every page of your PWA website. Browsers need to know what to show.

To fix the issue add the meta tag “theme_color” to your header within a -tag:

<meta name="theme-color" content="#ffffff"/>

As a side note, if you have a WordPress website, you can simply install a PWA plugin that automatically turns your website into a Progressive Web App.