Painless PWA
Tags: pwa, javascript, html, serviceworker
Published: 2020-05-17 05:41:36

---
title: Painless PWA
published: true
description: with minimal working example
tags: PWA, JS, HTML, ServiceWorker
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/unq8hwl9cdb7drqa1uim.png
---

> Disclaimer: PWA is still evolving, this article may soon become obsolete

An install-ready PWA is made of :

- an HTML page, with some specific `<meta>` and `<link>` tags
- a Service Worker script (`sw.js`), that will cache your assets for offline use.
- some icons for desktop and splash screen
- a `manifest.json`, that will describe your PWA to the installer.

## Minimal manifest.json example

```json
{
  "name": "Name",
  "short_name": "Short",
  "theme_color": "#1d3557",
  "background_color": "#ededed",
  "display": "standalone",
  "start_url": "./",
  "icons": [
    {
      "src": "192x192.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "512x512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ]
}
```

The main downside of the `manifest.json` is that it simply repeat what you've already set in your `index.html` (icons, name, theme_color ...).

A simpler solution is to automatically generate it from those tags while registering your serviceWorker :

# index.html

```html
<!doctype html>
<html lang=en>
<meta charset="UTF-8">
<title>title</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="theme-color" content="#317EFB"/>
<meta name="short-name" content="short"/>
<meta name="background-color" content="#FFFFFF"/>
<meta name="display" content="standalone"/>
<meta name="description" content="description"/>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<link rel="apple-touch-icon" href="192x192.png">
<link rel="splash-icon" href="512x512.png">
<link rel="worker" href="sw.js">
<script type=module defer>
const qsa=s=>[...document.querySelectorAll(s)];
const i=(s,x)=>({src:qsa(`[rel=${s}-icon]`)[0].href,type:"image/png",sizes:`${x}x${x}`});
//TODO: submit this code to iojscc
navigator.serviceWorker&&navigator.serviceWorker.register(qsa('[rel=worker]')[0].href)
.then(sw=>document.head.appendChild(Object.assign(document.createElement("link"),
{rel:"manifest",href:'data:,'+JSON.stringify(qsa('meta[name]').reduce((a,m)=>
(a[m.name.replace(/-/g,'_')]=encodeURIComponent(m.content),a),{start_url:sw.scope,
name:qsa('title')[0].innerText,icons:[i('apple-touch',192),i('splash',512)]}))})));
</script>

<h1>I'm PWA</h1>
```

# sw.js

```js
const cacheList = ["./"]; // add more if needed
const cacheName = "app-42";
self.addEventListener('install', e => e.waitUntil(caches.open(cacheName).then(cache => cache.addAll(cacheList).then(() => self.skipWaiting()))));
self.addEventListener('activate', e => e.waitUntil(self.clients.claim()));
self.addEventListener('fetch', e => e.respondWith(caches.open(cacheName).then(cache => cache.match(e.request, {ignoreSearch: true})).then(response => (e.request.cache !== 'only-if-cached' || e.request.mode === 'same-origin') ? (response || fetch(e.request)) : response)));
```

You shall get a perfect audit score (if your server support HTTP/2)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/auv6kgkv1vwmcj8wzeez.png)