Learn how to build a PWA in 5 minutes

One of the most important characteristics of Progressive Web Apps (PWAs) is the “progressive”. You don’t have to implement every feature of a PWA to consider your site a PWA. Instead, the idea is that you implement a range of steps, each making your app better for your end users.

This blog post makes a key assumption. There are many developers out there who want to make their apps progressively better, but they want to do it in 5 minutes or less. And that’s what this blog aims to show you.

It’s a very basic PWA hello-world, showing you a set of simple steps to re-work your own app. Words are kept to a minimum, linking to docs for those who need or want to read more. Feel free to skip the blog altogether, and just clone the app: https://github.com/Meggin/hello-world-pwa.

Create responsive hello-world app

Include viewport and stylesheet link in index.html (see also https://codepen.io/Meggin/pen/wrJgOX):

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My hello world page</title>
<link rel="stylesheet" type="text/css" href="hello-world.css" media="all">
</head>
<body>
<h1 class="vertical-container">Hello World</h1>
</body>
</html>

Use flexbox in hello-world.css to center text responsively:

body {
background-color: #FF9800;
color: black;
}
.vertical-container {
height: 300px;
display: -webkit-flex;
display: flex;
-webkit-align-items: center;
align-items: center;
-webkit-justify-content: center;
justify-content: center;

}
h1.vertical-container {
font-size: 275%;
}

Host with Firebase

Create Firebase project

Create Firebase project in Firebase console and add project to index.html:

IMPORTANT: you will need to replace all Firebase project details in sample code throughout this blog with your own Firebase project details.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#008000"/>
<title>My hello world page</title>
<link rel="stylesheet" type="text/css" href="hello-world.css" media="all">
</head>
<body>
<h1 class="vertical-container">Hello World</h1>
<script src="https://www.gstatic.com/firebasejs/4.4.0/firebase.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: "AIzaSyAXK4Orxl2CghIQvKiUPtkhEngSgzteqE0",
authDomain: "hello-world-pwa-8669c.firebaseapp.com",
databaseURL: "
https://hello-world-pwa-8669c.firebaseio.com",
projectId: "hello-world-pwa-8669c",
storageBucket: "hello-world-pwa-8669c.appspot.com",
messagingSenderId: "660239288739"
};
firebase.initializeApp(config);
</script>

</body>
</html>

Initialize Firebase project

Make sure node.js is installed and then run:

$ npm install -g firebase-tools
$ firebase init    # Generate a firebase.json (REQUIRED)

Select Hosting:

Guided by Firebase, select your project, change public directory to src, configure as single-page app, and DO NOT overwrite index.html.

Deploy Firebase project

$ firebase deploy

Go to URL and check it looks like this:

Run Lighthouse

Get Lighthouse extension and run lighthouse tool on hosted site:

Register Service Worker

Add service worker <script> to index.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#008000"/>
<title>My hello world page</title>
<link rel="stylesheet" type="text/css" href="hello-world.css" media="all">
</head>
<body>
<h1 class="vertical-container">Hello World</h1>
<script src="https://www.gstatic.com/firebasejs/4.4.0/firebase.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: "AIzaSyAXK4Orxl2CghIQvKiUPtkhEngSgzteqE0",
authDomain: "hello-world-pwa-8669c.firebaseapp.com",
databaseURL: "https://hello-world-pwa-8669c.firebaseio.com",
projectId: "hello-world-pwa-8669c",
storageBucket: "hello-world-pwa-8669c.appspot.com",
messagingSenderId: "660239288739"
};
firebase.initializeApp(config);
</script>
<script>
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function() {
console.log('Service Worker Registered');
});
}
</script>

</body>
</html>

Cache Static Resources

Create sw.js and add index.html and hellow-world-pwa.css to hello-world-page cache:

var cacheName = 'hello-world-page';
var filesToCache = [
'/',
'/index.html',
'/hello-world.css'
];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(filesToCache);
})
);
});
self.addEventListener('activate',  event => {
event.waitUntil(self.clients.claim());
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request, {ignoreSearch:true}).then(response => {
return response || fetch(event.request);
})
);
});

Test resources cached

Re-deploy, open Chrome DevTools, and check Cache storage under Application tab:

Create an icon

Create an icon and save in sizes 128x128, 144x144, 152x152, 192x192, 512x512.

Create manifest.json

To support add to homescreen feature, create manifest.json:

{
"name": "Hello World PWA",
"short_name": "Hi",
"icons": [{
"src": "icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
}, {
"src": "icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
}, {
"src": "icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
}, {
"src": "icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "/index.html",
"gcm_sender_id": "103953800507",
"display": "standalone",
"background_color": "#FF9800",
"theme_color": "#FF9800"
}

Then link to manifest.json in index.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#008000"/>
<title>My hello world page</title>
<link rel="stylesheet" type="text/css" href="hello-world.css" media="all">
<link rel="manifest" href="manifest.json">
</head>
<body>
<h1 class="vertical-container">Hello World</h1>
<script src="https://www.gstatic.com/firebasejs/4.4.0/firebase.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: "AIzaSyAXK4Orxl2CghIQvKiUPtkhEngSgzteqE0",
authDomain: "hello-world-pwa-8669c.firebaseapp.com",
databaseURL: "https://hello-world-pwa-8669c.firebaseio.com",
projectId: "hello-world-pwa-8669c",
storageBucket: "hello-world-pwa-8669c.appspot.com",
messagingSenderId: "660239288739"
};
firebase.initializeApp(config);
</script>
<script>
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function() {
console.log('Service Worker Registered');
});
}
</script>
</body>
</html>

Set up Push Notification

Add user permission request to index.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#008000"/>
<title>My hello world page</title>
<link rel="stylesheet" type="text/css" href="hello-world.css" media="all">
<link rel="manifest" href="manifest.json">
</head>
<body>
<h1 class="vertical-container">Hello World</h1>
<script src="https://www.gstatic.com/firebasejs/4.4.0/firebase.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: "AIzaSyAXK4Orxl2CghIQvKiUPtkhEngSgzteqE0",
authDomain: "hello-world-pwa-8669c.firebaseapp.com",
databaseURL: "https://hello-world-pwa-8669c.firebaseio.com",
projectId: "hello-world-pwa-8669c",
storageBucket: "hello-world-pwa-8669c.appspot.com",
messagingSenderId: "660239288739"
};
firebase.initializeApp(config);
</script>
<script>
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function() {
console.log('Service Worker Registered');
});
}
</script>
<script>
const messaging = firebase.messaging();
      messaging.requestPermission()
.then(function() {
console.log('Notification permission granted.');
return messaging.getToken();
})
.then(function(token) {
console.log(token);
})
.catch(function(err) {
console.log('Unable to get permission to notify.', err);
})
</script>

</body>
</html>

Get service worker to handle push events in new file, firebase-messaging-sw.js:

importScripts('https://www.gstatic.com/firebasejs/4.4.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.4.0/firebase-messaging.js');
var config = {
apiKey: "AIzaSyAXK4Orxl2CghIQvKiUPtkhEngSgzteqE0",
authDomain: "hello-world-pwa-8669c.firebaseapp.com",
databaseURL: "https://hello-world-pwa-8669c.firebaseio.com",
projectId: "hello-world-pwa-8669c",
storageBucket: "hello-world-pwa-8669c.appspot.com",
messagingSenderId: "660239288739"
};
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
const title = 'Hello World';
const options = {
body: payload.data.body
};
return self.registration.showNotification(title, options);
});

Test Push Notification

To set up push notifications using Firebase Cloud Messaging:

  1. Get your Firebase project’s server key (thanks, Stack Overflow):
  2. Click the settings icon/cog wheel next to your project name at the top of the new Firebase Console.
  3. Click Project Settings.
  4. Click the Cloud Messaging tab. Copy key under Server Key.
  5. Redeploy your Firebase app: $ firebase deploy.
  6. Open Chrome DevTools console, and copy the token in the console log.
  7. Test push notification from the command line (replace YOUR_SERVER_KEY with the copied key and DEVICE_REGISTRATION_TOKEN with the copied token):
curl -X POST -H "Authorization: key=YOUR_SERVER_KEY" -H "Content-Type: application/json" -d '{
"notification": {
"title": "Hello World PWA",
"body": "Hi",
},
"to": "DEVICE_REGISTRATION_TOKEN"
}'
"https://fcm.googleapis.com/fcm/send"

Run Lighthouse Again

Deploy and run Lighthouse again. It’s not perfect but it is progressively enhanced:

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.