Offline is becoming a standard thing to do in web applications because they allow your website to feel like an app. In the modern web most things are doable in an app can now be done on the web.
The progressive web app follows 3 principles:
- Reliable — the application needs to load instantly and never show the offline dinosaur. The web application should be able to handle anything that is thrown at it so having a network fallback from the application is a must.
- Fast — it must respond quickly to user interactions. The animations should feel smooth and there are no “Jank” when the user scrolls down the page.
- Engaging — the user experience needs to feel app-like, keeping the user experience as simple as possible
In this article, I am about to show you a game I built which helps you understand what it takes to get the bare minimum progressive web app out to production. A progressive web app could take a long time to build which can be a difficult type of ticket to get through the board.
So how can we breakdown the bare minimum which will still deliver value to the customer?
Step one — Setting up
Firstly, let’s clone down the repository on our local machines. All we have to do is run
npm install and then
npm run start-l1. The folder directory for lesson one is in /starter-files/lesson-1. The project will now spin up on localhost:8080.
Next we need to get setup on somewhere that will host our application. I have used Now — Zeit to host my web application in https. At some point we need to start deploying our application so we can see the experience on our own devices. It is really easy to get setup follow this guide to get setup.
Step Two — Build a manifest.json
This section will tick off the following in the lighthouse PWA checklist:
- Sets an address-bar theme color
- Configured for a custom splash screen
- Web app manifest meets the installability requirements
A web manifest provides the browser with information for the application for when the user installs the app onto their home screen.
Let’s go through what our manifest.json file needs:
- The short_name is a string that represents the name of the web application displayed to the user if there is not enough space to display the full name.
- name is displayed when there is enough space. An example of this would be Joe Blog Business LTD and your short name could be JBB LTD.
- description is used to explain to the user what the app is about and what the app is capable of doing.
- start_url is the url of which will be opened when the user comes from the home screen to your web application. Pro tip — utm_source=homescreen is a good way to track which users come from the home screen to your web application in Google Analytics.
- display tells the application how to display. I use standalone because it will remove all UI features such as the browser bar at the top of any normal website. This makes the website feel more like an app because the whole screen is focused on just your website.
- background_color is used as a visual thing, when you open the application it will use the background colour from the manifest file.
- theme_color is used on the address bar of the application when it is installed.
- icons are used to display your logo on the splash screen of the web application. It comes in many sizes which are; 48x48, 72x72, 96x96, 144x144, 168x168, 192x192, 512x512.
Step Three — Offline Page
You made it this far! Awesome! Now we have done the easy part, let’s get into the nitty gritty part and make our web application have an offline page. Building an offline page requires us to build a service worker file.
A service worker file is a web worker which runs JavScript off the main thread. It has the power to cache files offline, proxy urls, push notifications and background syncs.
In the root of our project lets get a sw.js file made in the root of the project.
A service worker has three events which are; install, activate & fetch. These are the events which are more than likely in every service worker file as they are needed to make the service worker file install onto the users device.
What is going on here is that we are checking if service worker is supported by the browser and then it will try and register the file. After that, we need to build our install event in the sw.js file.
In our example we have a variable called cacheFiles which has our offline page, main.js, style.css & our pwa-lighthouse.png image.
The few lines below are in there so we can have version control on our service worker cache. This will then allow us to be more efficient with what we cache and it stops us from having dead cache assets. If we have one cache number it will just make the cache go bigger and bigger until the cache can no longer save our assets.
In my example it is just a hard coded number but in a production projects we would have something like the build number in there or update the version when one of the assets in the cache has changed.
In the install event what is happening is that we wait until the caches API has opened and then it will add all of our cache files into the new cache instance.
Pro tip — the next then chain is not needed but it is very useful to use self.skipWaiting because it tells the service worker to not wait for the old service worker to unregister it will automatically install itself over the previous one.
The next thing we need to do to keep our cache file size low is to delete all of the of caches the service worker has previously added on the customers device.
First thing we need to do is make a cache whitelist array that will stop the cache API from deleting the cache we created in our install step. Once done, it will loop through all of the cache names and delete the other caches from the application cache.
The fetch event is fired when a network request is made. The service worker now listens to all of the fetch events so it can proxy the requests if we need to.
In our example, we need to determine which requests are html requests. We do not want to respond with an offline page when the request was not an html request.
If it is not an html request then we will check if that request exists in our cache and if it does, respond with the cached version on the service worker else it will fetch off for it if it does not exist in there.
Now we can test this out by turning off our network connection in dev tools. Go to devtools / network and then select the offline checkbox. This will emulate an offline experience.
We are offline! So we have now a manifest json file which tells our progressive web app how the app should work and then we added a service worker which will provide an offline page for our html requests. Now let’s get our application deployed to a production environment so we can fully test if our application is 100% PWA optimised.
Deploying the application
What we now need to do is run
npm run deploy-l1 in our terminal. This will allow you to get access to the application on your phone and install it to your home screen. Let’s run a lighthouse test on our public domain.
We now have a fully working progressive web app.
As you can see, it doesn’t take much effort to get the bare minimum released to the user. Users can now start installing their applications on their devices and we can provide an offline page when the user has network difficulties.
- utm_source=offline is a good way to track when a user comes back from the offline page to the current page on the site. This is a good way to get some metrics on which users converted when they returned.
- “Gamify” the offline experience. Make your offline pages fun or insightful which rich content. An example would be to provide the business preposition on the offline page or even cache a few latest article blog posts to keep the user engaged.
- Use a template to build your service worker, it will help when it comes to adding in version control and generate assets.
- Webpack manifest plugin can be useful to get access to the public files. Especially when you start to implement md5 caching.
Thank you for reading!