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
{
"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
<!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
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)
Top comments (6)
Great MVP, I have only turned on pwa via gatsby plugin. Cool to get a taste of how it gets implemented.
Nice ,is there any PWA starter tempale like "htmlBoilerPlate" but with PWA ?
Replace the manifest generator
<script>
with the example manifest.json you will get the simplest installable PWA that pass the audit criterias.I tested on desktop chrome, but not on others platforms/browsers.
What is the difference (pros / cons) between link rel="worker" and script src="worker" implementation?
<script src=sw.js></script>
will not register the script as a Service Worker (running in a background thread).You need to
navigator.serviceWorker.register('/path/to/your/worker.js')
for that.I used
<link rel="worker" href="sw.js">
because it allows me to give a relative URL, but get an absolute URL when accessing the hrefattribute
.Simply awesome