Turn Your Website into a PWA

Discover how to make a progressive web app in 5 mins and why you need it

Luca Spezzano
Oct 8 · 7 min read
Photo by Yura Fresh on Unsplash

According to this article by CNBC, Nearly three-quarters of the world will use just their smartphones to access the internet by 2025.

Considering these stats, if you are thinking of building a new website or redesign one, you should consider developing your website as PWA with no doubts.

Starting from a PWA could be the perfect start for your projects, you don’t need too much effort and you can build one application that works for all platforms!

What is a PWA?

You can add them to the home of your phone exactly as a mobile app

Why should you build a PWA?

A Progressive Web App can cut load times, allow users to navigate offline, increase the time spent on the website, increase revenue, can be much smaller than a mobile app, and much more.

Who is already using PWA?

Uber, Instagram, Twitter, Pinterest, Forbes, Alibaba

It’s interesting to see the companies which already have a PWA, some of the world’s biggest company such as Twitter, Instagram, Uber, Pinterest, Forbes, Alibaba and much more.

How to make a PWA

The Web App Manifest

The properties:

  • name: is the name used in the app install prompt
  • short_name: is the name used on the user’s home screen, launcher, or other places where space may be limited
  • start_url: tells the browser where your application should start when it is launched
  • display: it allows us to customize what browser UI is shown when your app is launched. The most used value is standalone: it opens the web app to look and feel like a standalone native app.
  • background_color: is used on the splash screen when the application is launched
  • theme_color: it sets the color of the toolbar
  • orientation: it allows to enforce a specific orientation
  • scope: to be honest usually I don’t use this property, but it defines the set of URLs that the browser considers being within your app and often it’s used to decide when the user has left the app
  • icons: when a user adds your site to their home screen, you can define a set of images for the browser to use

The Service Worker

  • intercepting network requests
  • caching or retrieving resources from the cache
  • delivering push messages

Service worker lifecycle

  • Installation: triggers an install event where we can run some tasks when the service worker installs.
  • Activation: if there are any open pages controlled by the previous service worker, the new service worker enters a waiting state. The new service worker only activates when there are no longer any pages loaded that are still using the old service worker and can be used to run some tasks too.

Let’s build our PWA step by step in 5 minutes

Before to start I suggest you install the Lighthouse extension.
Lighthouse is a tool (by Google) for improving the quality of web pages, and it gives you a report like this

Lighthouse report

and from there, you can check which issues you need to solve to improve performance, accessibility, best practices, SEO and PWA of your website or web app.

You can install the extension for Chrome here.

The file structure in our example will look like this

file structure

You will find the full code on Github here, and you can switch branches to see the dynamic or the static cache.

In our index.html let’s call our manifest.json

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

But we also need to call our app.js file(where we will register our service worker) and some meta tag necessary to optimize our PWA

<link rel="apple-touch-icon" href="/assets/images/logo-96x96.png">
<meta name="apple-mobile-web-app-status-bar" content="#FFE1C4">
<meta name="theme-color" content="#FFE1C4">
<script src="/assets/js/app.js"></script>

these are the main tags, but of course, we will have more, and they can also have different paths, it is up to you!

Cache static

This method is useful, especially when you want to download all the resources of your website and to cache them at the first landing on the page.

Let’s start with our manifest.json

{
"name": "Name Website",
"short_name": "NameWebsite",
"start_url": "/index.html",
"display": "standalone",
"background_color": "#F4F4F4",
"theme_color": "#F4F4F4",
"orientation": "portrait-primary",
"icons": [
{
"src": "/assets/images/logo-72x72.png",
"type": "image/png",
"sizes": "72x72"
},
{
"src": "/assets/images/logo-96x96.png",
"type": "image/png",
"sizes": "96x96"
},
{
"src": "/assets/images/logo-128x128.png",
"type": "image/png",
"sizes": "128x128"
},
{
"src": "/assets/images/logo-144x144.png",
"type": "image/png",
"sizes": "144x144"
},
{
"src": "/assets/images/logo-152x152.png",
"type": "image/png",
"sizes": "152x152"
},
{
"src": "/assets/images/logo-192x192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/assets/images/logo-384x384.png",
"type": "image/png",
"sizes": "384x384"
},
{
"src": "/assets/images/logo-512x512.png",
"type": "image/png",
"sizes": "512x512"
}
]
}

After we need to check if the browser allows service workers, and if so, we will register our service worker in our app.js.

if('serviceWorker' in navigator){
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('service worker registered'))
.catch(err => console.log('service worker not registered', err));
}

Now let’s write our service worker in our sw.js file

const staticCacheName = 'site-static-v1';
const assets = [
'/',
'/index.html',
'/assets/js/ui.js',
'/assets/css/main.css',
'/assets/images/background-home.jpg',
'https://fonts.googleapis.com/css?family=Lato:300,400,700',
];
// install event
self.addEventListener('install', evt => {
evt.waitUntil(
caches.open(staticCacheName).then((cache) => {
console.log('caching shell assets');
cache.addAll(assets);
})
);
});
// activate event
self.addEventListener('activate', evt => {
evt.waitUntil(
caches.keys().then(keys => {
return Promise.all(keys
.filter(key => key !== staticCacheName)
.map(key => caches.delete(key))
);
})
);
});
// fetch event
self.addEventListener('fetch', evt => {
evt.respondWith(
caches.match(evt.request).then(cacheRes => {
return cacheRes || fetch(evt.request);
})
);
});

We store in the array all the resources that we want to put in our cache

Install event

cache screen

Activate event

Fetch event

network screen

ps. When we change our service worker file, we need to change the name of the cache. This allows us to update our service worker and create a new cache.

Dynamic cache

All the configuration that you have seen before it’s perfectly good you need to change your sw.js file with this:

const dynamicCacheName = 'site-dynamic-v1';// activate event
self.addEventListener('activate', evt => {
evt.waitUntil(
caches.keys().then(keys => {
return Promise.all(keys
.filter(key => key !== dynamicCacheName)
.map(key => caches.delete(key))
);
})
);
});
// fetch event
self.addEventListener('fetch', evt => {
evt.respondWith(
caches.match(evt.request).then(cacheRes => {
return cacheRes || fetch(evt.request).then(fetchRes => {
return caches.open(dynamicCacheName).then(cache => {
cache.put(evt.request.url, fetchRes.clone());
return fetchRes;
})
});
})
);
});

Active event

Fetch event

If you try both the cache and after you run Lighthouse, you can see that your website is a WPA.

PWA Lighthouse

You can find the code of static and dynamic caches here.

Which one should I use static or dynamic?

You can read more about PWA on google developers here.

NotOnlyCSS

This publication includes original articles and tips about frontend technologies.

Luca Spezzano

Written by

Frontend developer focused on CSS architecture of scalable and maintainable large scale projects and the development of amazing user interfaces.

NotOnlyCSS

This publication includes original articles and tips about frontend technologies.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade