PWA Best Practices

Louis Japheth Kouassi
5 min readAug 4, 2023

--

PWA Best Practices
PWA Best Practices

Progressive Web Apps (PWAs) are a type of web app that combines the best features of web apps and native apps. They are installed on the device’s home screen, can be used offline, and provide a similar user experience to native apps.

  • If you are new to PWA, I wrote an article to help you find out which you can read here.
  • If you already have notions in PWA and you want to quickly integrate it into your website to make it a web application, you can read this article I wrote. It will help you as best as possible.

In this article, I will give you everything you need to make your PWA operational, unstable on all devices (Android, Apple, Watch, Tablet…), and available offline.

At the end of this article, you will be able to :

So, let’s start 😉!

How to create a manifest file ?

  • Create a manifest.json or manifest.webmanifest and fill in its properties;
an example of Manifest with its properties👇
{
"name": "Progressive Web App",
"short_name": "PWA",
"description": "A web application manifest (web manifest) is a JSON file that provides information about a progressive web application (PWA). The manifest is used by browsers to display the Progressive Web App in the app list, to provide information about the app to the user, and to allow the app to work offline.",
"lang": "fr-FR",
"dir": "auto",
"related_applications": [{
"platform": "webapp",
"url": "https://pwa-example.com/manifest.webmanifest"
}],
"prefer_related_applications": true,
"categories": ["social", "productivity", "news"],
"share_target": {
"action": "/share-target/",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"title": "PWA News",
"text": "A web application manifest (web manifest) is a JSON file that provides information about a progressive web application (PWA). The manifest is used by browsers to display the Progressive Web App in the app list, to provide information about the app to the user, and to allow the app to work offline.",
"url": "https://pwa-example.com/"
}
},
"shortcuts": [
{
"name": "Page principale",
"short_name": "Description",
"url": "/index.html",
"icons": [
{ "src": "/static/icons/192x192.png", "sizes": "192x192", "type": "image/png" }
]
},
{
"name": "About",
"short_name": "about page",
"url": "/about.html",
"icons": [
{ "src": "/static/icons/192x192.png", "sizes": "192x192", "type": "image/png" }
]
},
{
"name": "Contact",
"short_name": "Contact",
"url": "/contact.html",
"icons": [
{ "src": "static/icons/192x192.png", "sizes": "192x192", "type": "image/png" }
]
},
],
"display_override": [
"window-controls-overlay"
],
"edge_side_panel": {
"preferred_width": 496
},
"icons": [
{
"src": "assets/images/app/android/android-launchericon-512-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "assets/images/app/ios/256.png",
"sizes": "256x256",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "assets/images/app/ios/16.png",
"sizes": "16x16",
"type": "image/png",
"purpose": "maskable"
}
],
"start_url": "/",
"theme_color": "#4285f4",
"background_color": "#dbdad9",
"display": "standalone",
"orientation": "portrait",
"scope": "/"
}
],
"widgets": [
{
"name": "PWAmp mini player",
"short_name": "PWAmp",
"description": "Widget to control the PWAmp player",
"tag": "pwamp",
"ms_ac_template": "widgets/mini-player.json",
"data": "widgets/mini-player-data.json",
"screenshots": [
{
"src": "./screenshot-widget.png",
"sizes": "600x400",
"label": "The PWAmp mini-player widget"
}
],
"icons": [
{
"src": "./favicon-48.png",
"sizes": "48x48"
},
{
"src": "./favicon-96.png",
"sizes": "96x96"
},
{
"src": "./favicon-128.png",
"sizes": "128x128"
},
{
"src": "./favicon-256.png",
"sizes": "256x256"
},
{
"src": "./favicon-512.png",
"sizes": "512x512"
}
],
"backgrounds": [
{
"src": "./widgets/background.png",
"sizes": "600x400"
}
]
}
]
}

Use this site to generate the icons for all platforms like this 👇

Use this site to generate the slapsh-screen for all platforms like this 👇

  • In your main file (index.html), insert this code 👇
<link rel="manifest" href="/manifest.json">
or
<link rel="manifest" href="/manifest.webmanifest">

How to create a service-worker file ?

Create a file and name it like this: service-worker.js. Import this code and modify it as needed :

importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js');
const VERSION = "version";
const CACHE_NAME = `version-${VERSION}`;

if (workbox) {
console.log(`Yay! Workbox is loaded 🎉`);

workbox.core.setCacheNameDetails({
prefix: 'yourPWAname',
suffix: 'version',
precache: 'precache',
runtime: 'run-time',
});

workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);

// Cache the Google Fonts stylesheets with a stale while revalidate strategy.
workbox.routing.registerRoute(
/^https:\/\/fonts\.googleapis\.com/,
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'google-fonts-stylesheets',
}),
);

// Cache the Google Fonts webfont files with a cache first strategy for 1 year.
workbox.routing.registerRoute(
/^https:\/\/fonts\.gstatic\.com/,
new workbox.strategies.CacheFirst({
cacheName: 'google-fonts-webfonts',
plugins: [
new workbox.cacheableResponse.Plugin({
statuses: [0, 200],
}),
new workbox.expiration.Plugin({
maxAgeSeconds: 60 * 60 * 24 * 365,
}),
],
}),
);
workbox.routing.registerRoute(
new RegExp('/css/'),
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'css-cache',
plugins: [
new workbox.expiration.Plugin({
// Only cache requests for a week
maxAgeSeconds: 15 * 24 * 60 * 60 * 365,
// Only cache requests.
maxEntries: 10,
}),
]
})
);

workbox.routing.registerRoute(
new RegExp('/js/'),
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'js-cache',
plugins: [
new workbox.expiration.Plugin({
// Only cache requests for a week
maxAgeSeconds: 15 * 24 * 60 * 60 * 365,
// Only cache requests.
maxEntries: 10,
}),
]
})
);
workbox.routing.registerRoute(
new RegExp('/media/'),
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'img-cache',
plugins: [
new workbox.expiration.Plugin({
// Only cache requests for a week
maxAgeSeconds: 15 * 24 * 60 * 60 * 365,
// Only cache requests.
maxEntries: 10,
}),
]
})
);


} else {
console.log(`Boo! Workbox didn't load 😬`);
}

self.addEventListener("install", (event) => {
event.waitUntil(
caches
.open("v1")
.then((cache) =>
cache.addAll([
"/",
"/index.html",
"/style.css",
"/app.js",
"/manifest.webmanifest",
"/service-worker.json",
"/workbox-config.js",
"/app.js"
]),
),
);
});

self.addEventListener("fetch", (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
// caches.match() always resolves
// but in case of success response will have value
if (response !== undefined) {
return response;
} else {
return fetch(event.request)
.then((response) => {
// response may be used only once
// we need to save clone to put one copy in cache
// and serve second one
let responseClone = response.clone();

caches.open("v1").then((cache) => {
cache.put(event.request, responseClone);
});
return response;
})
.catch(() => caches.match("/img/img1.jpg"));
}
}),
);
});

How to create a workbox-config.js file ?

Create a file and name it like this: workbox-config.js. Import this code and modify it as needed :

module.exports = {
globDirectory: 'public/',
globPatterns: [
'**/*.{html,css,ttf,woff,woff2,js,pdf,eps,png,docx,txt,mp3,xml,ico,svg,jpg,eot,gif,rb,less,scss}'
],
maximumFileSizeToCacheInBytes: 700,
swDest: 'public/service-worker.js',
swSrc: 'src/service-worker.js',
globIgnores: [
'**/node_modules/**/*'
]
};

const workbox = require('workbox-build');

// Configure les caches pour les ressources de base.
workbox.precaching.precacheAndRoute([
{
path: '/',
revision: '1',
},
{
path: '/images/logo.png',
revision: '1',
},
{
path: '/scripts/app.js',
revision: '1',
},
{
path: '/styles/main.css',
revision: '1',
},
]);

// Configure les caches pour les ressources des API.
workbox.routing.registerRoute(
new RegExp('^/api/.*'),
new workbox.strategies.CacheFirst({
cacheName: 'api',
maxAgeSeconds: 60 * 60 * 24 * 7, // 1 semaine
}),
);

// Configure les caches pour les ressources des images.
workbox.routing.registerRoute(
new RegExp('^/images/.*'),
new workbox.strategies.CacheFirst({
cacheName: 'images',
maxAgeSeconds: 60 * 60 * 24 * 365, // 1 an
}),
);

// Configure les caches pour les ressources des styles.
workbox.routing.registerRoute(
new RegExp('^/styles/.*'),
new workbox.strategies.CacheFirst({
cacheName: 'styles',
maxAgeSeconds: 60 * 60 * 24 * 365, // 1 an
}),
);

And now your PWA is ready to be deployed and installed on all mobile devices, tablets, connected watches and laptops !

--

--

Louis Japheth Kouassi

I am passionate about Google Firebase, PWA, AI/ML Techs. I like to learn, share and learn from others. GDG+TFUG Bassam Lead! Twitter: @LouisKouassii