Create a Simple Progressive Web App (PWA)


GitHub Link:

What should you expect ?

  • We will be going through an example using http://localhost:4000/

Why should we use Progressive web app at all ?

  • To give an native app experience for tablets and mobile
  • Add offline support for the app
  • Support for push notification (not covered)
  • Many more

Progressive Web App Checklist

  • The website needs to be served over https (use if you want to mimic https for development)
  • The website can also be served using localhost
  • Service worker will not work in http
  • App needs to have a manifest file
  • App needs to have a working service worker


  • Service Worker Communication
  • Manifest File
  • Service Worker Events

Service Worker Communication

  • Therefore, service worker [sw.js] has no access to global “window” object
  • Create a separate service worker file [sw.js]
  • On the other hand, [main.js] will have the access to “window” object
  • We will be registering service worker [sw.js] from [main.js]
  1. Install few packages
npm init -y
npm install express
npm install --save-dev nodemon

2. Create a folder structure like Fig: 1

Fig 1: Folder Structure

3. Simple Node Server (index.js)

npm run dev
Fig: 2 Serve static files and create a route

4. Route “/” => index.html

Fig 3: Simple HTML File

5. Create main.js inside static folder

  • Detect, if the browser supports service worker or not
  • Looks for the sw.js
  • Generates a list of post
Fig 4: Registering sw.js from the main.js

6. Add the simple css

Fig 5: Simple Design

You would able to see something like this

Fig 6: Simple PWA App with console.log

Manifest File

  • Check your console and check the Installability [Fig: 7], it will ask for the missing pieces
  • You can always generate manifest.json using manifest generator from other websites or app
  • Before generating, make sure you have a logo which is 1:1 ratio and (at least) 512 X 512 pixels in size.
  • “start_url” is a string which represents the location when a user taps a icon in mobile or tablets to where should it first go (home screen usually)
  • “scope” is a string which restricts navigation to a sub directory of the site
  • Therefore, the files needs to available in the following address bar url locations
start url: http://localhost:4000/
manifest url: http://localhost:4000/manifest.json
Since our scope is "/" all the files needs to be in
other files urls (example): http://localhost:4000/main.js
Fig 7: Installability Issues
Fig 8: Updated Manifest File

Console -> Application -> Manifest

Fig 9: With the updated manifest json


  • Go to console then to Lighthouse
  • Check mark only the Progressive Web App
  • Generate a report
  • If there are any issues, fix it accordingly
Fig 10: Lighthouse Report

Service Worker Events

  • Inside sw.js, the service worker go through install event
  • In install event, we can decide what files we want to cache
  • In activate event, usually it is best to remove the previous caches or deprecated ones
  • In fetch event, especially for offline app, we can decide how we want to save the response and return the response of the api urls
Fig 11: Service Worker Connection
Fig 12: Added three mechanisms in sw.js

Now you can see something like these in your chrome browser

Fig 13: Chrome Browser Install the app option
Fig 14: Android Emulator, Install the app option
Fig 15: IOS

Now the native experience for the tablets or mobile is ready but to give offline experience we need to go little deeper.


  • Cache list consists of static files
  • Static cache version is the key where all the CACHE_LIST will be saved
Fig 16: Utilizing Install Event

Install Event

  • Inside sw.js, caches which is a global var (readily available)
  • If you console.log the caches you will find it has few__proto__ which includes delete, has, keys, match, open
  • If you console.log the cache [these is from] you will find it has few __proto__ which includes add, addAll, delete, keys, match, matchAll, put
  • Storing the resources in CACHE_LIST
Fig 17: Utilizing Install Event

Activate Event

  • Mostly we remove the outdated caches
Fig 18: Utilizing Activate Event

Fetch Event

  • This is where we define how the files are supposed to serve from the cache
const CACHE_LIST = [
" bootstrap/4.5.3/css/bootstrap.min.css",

Cache Only

  • We can only serve the CACHE_LIST files
  • Show error if the request do not match any files from CACHE_LIST
eg: '/index.html'

Cache then network

  • If the request matches the CACHE_LIST respond
  • If it is not available in CACHE_LIST, fetch it from the api
  • Save the response from the fetch to the CACHE_LIST
  • Then return the response
eg: ''

Network then Cache

  • No matter what the request is, first fetch
  • If there is a success fetch response, return the response and update the cache
  • If there is fetch error, return the response from the cache if the request matches the one of the items in CACHE_LIST

I would use the network then cache over here

Fig 19: Utilizing Fetch Event

Now the app will work offline

Fig 20: Dynamically Caching

Over here as you can see the “/posts/” as well other images files has been cached because of how we setup the fetch event.

Some Caveats

  • Debugging process for the service worker is sometimes tedious
  • When you refresh the page the new updated sw.js will be installed but it wont be activated. Therefore, you wont be able to see the changes for the new sw.js instantly.
  • For this reason you need to close all the current working tabs and open a new tab.
  • Alternatively you check mark the updated on reload in service worker console in google chrome
  • Also feel free to click Unregister and skipWaiting and see how it works out for you.
  • It is also better, to turn off the service worker during development using .env file or process.env.NODE_ENV because it might hamper your other work

I have passion for UI/UX, animations and love to create websites to run across multiple devices with dynamic user experiences

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store