Making GitLab a PWA
Or, how I learned to stop worrying and embrace the boring solution.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/samdbeckham/images/1254654/me.jpeg)
Sam Beckham
@samdbeckham
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/1342709/frontend-ne-block-icon-avatar.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5789146/icon.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5789154/icon__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4041727/2018.frontendne.co.uk-_Nexus_5X_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4041739/2018.frontendne.co.uk-_iPad___3_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4041727/2018.frontendne.co.uk-_Nexus_5X_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4041727/2018.frontendne.co.uk-_Nexus_5X_.png)
3G
WiFi
1s
3s
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4041727/2018.frontendne.co.uk-_Nexus_5X_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4041727/2018.frontendne.co.uk-_Nexus_5X_.png)
3G
WiFi
0.3s
0.3s
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4041727/2018.frontendne.co.uk-_Nexus_5X_.png)
Offline
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4058306/data-text-html_chromewebdata_Nexus_5X_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4041727/2018.frontendne.co.uk-_Nexus_5X_.png)
3G
WiFi
0.3s
0.3s
0.3s
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4041727/2018.frontendne.co.uk-_Nexus_5X_.png)
It's a
website
that acts like a
native app
Install to
your device
Load offline
It's a
progressive enhancement
Just a
website
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5813816/Screenshot_2019-02-22_at_18.28.20.png)
The
boring
solution
Lighthouse
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5801313/Screenshot_2019-02-19_at_23.39.19.png)
✅ Non-Javascript content
✅ https
✅ https redirect
✅ Fast enough on 3g
✅ <meta viewport> tag
✅ Sized for the viewport
❌ Custom splash screen
❌ Themed Address Bar
❌ Service Worker
❌ 200 When offline
❌ Prompted to install the web app
Lighthouse
❌
Custom Splash Screen
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4098553/twotter.jpg)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4090780/2018.frontendne.co.uk-_Nexus_5X_.png)
❌
Themed Address bar
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4098483/Screenshot_1504631621.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4098484/Screenshot_1504631639.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4098482/Screenshot_1504631594.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4098480/Screenshot_1504631581.png)
{
"name": "GitLab",
"short_name": "GitLab",
"display": "standalone",
"scope": "/",
"start_url": "/",
"theme_color": "#474D57",
"background_color": "#ffffff",
"icons": [
{
"src": "touch-icon-ipad-retina.png",
"sizes": "152x152",
"type": "image/png"
}
]
}
./manifest.json
<head>
<!-- other head stuff -->
<link rel="manifest" href="/manifest.json">
</head>
./yourpage.html
Lighthouse
✅ Custom splash screen
✅ Themed Address Bar
❌ Service Worker
❌ 200 When offline
❌ Prompted to install the web app
❌
Service worker
💻
🌐
Browser
Server
request
response
💻
🌐
Browser
Server
🐲
Service
Worker
💻
🌐
Browser
Server
🐲
Service
Worker
cat.png
cat.png
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4090785/cat.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4090785/cat.gif)
💵
Cache
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4090785/cat.gif)
💻
🌐
Browser
Server
🐲
Service
Worker
cat.png
cat.png
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4090785/cat.gif)
💵
Cache
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4090785/cat.gif)
💤
❌
200 when offline
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5813816/Screenshot_2019-02-22_at_18.28.20.png)
The
boring
solution
❌
200 when offline
The
boring
solution
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5813851/Screenshot_2019-02-22_at_18.35.41.png)
💻
🌐
Browser
Server
🐲
Service
Worker
offline.html
💵
Cache
💻
🌐
Browser
Server
🐲
Service
Worker
issue
offline
💵
Cache
issue
❌
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5789114/2408304_0.jpg)
💧
// Register a service worker if our
// browser allows it
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(
'/service_worker.js',
{ scope: '/'}
);
}
Register
const CURRENT_CACHE = '...'
self.addEventListener('install', event => {
event.waitUntil(
caches
.open(CURRENT_CACHE)
.then(cache =>
cache.add('/offline'))
)
})
Install
./service_worker.js
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cache =>
cache !== CURRENT_CACHE
? caches.delete(cache)
: Promise.resolve()
),
...
})
Activate
./service_worker.js
self.addEventListener('fetch', ({request}) => {
event.respondWith(
fetch(request).catch(() => (
caches.match('/offline')
))
)
})
Fetch
./service_worker.js
const CURRENT_CACHE = '<%= Gitlab.version %>_<%= Gitlab.revision %>';
// eslint-disable-next-line no-restricted-globals
self.addEventListener('install', event => {
event.waitUntil(caches.open(CURRENT_CACHE).then(cache => cache.add('/offline')));
});
// eslint-disable-next-line no-restricted-globals
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cache =>
cache !== CURRENT_CACHE ? caches.delete(cache) : Promise.resolve(),
),
);
}),
);
});
// eslint-disable-next-line no-restricted-globals
self.addEventListener('fetch', { request } => {
event.respondWith(
fetch(request).catch(() => (
request.mode === 'navigate' ? caches.match('/offline') : null
)),
);
});
./service_worker.js
Lighthouse
❌ Prompted to install the web app
✅ Service worker
✅ 200 when offline
✅ https
✅ http => https
✅ Custom Splash Screen
✅ Themed Address bar
✅ non-javascript content
✅ Fast enough on 3g
✅ <meta viewport> tag
✅ Sized correctly for the viewport
❌
Prompted to install the web app
Do literally nothing else
✅ Service worker
Lighthouse
✅ 200 when offline
✅ https
✅ http => https
✅ Prompted to install the web app
✅ Custom Splash Screen
✅ Themed Address bar
✅ non-javascript content
✅ Fast enough on 3g
✅ <meta viewport> tag
✅ Sized correctly for the viewport
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/4019294/Loading_icon.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5832627/Desktop_PWA_.png)
Gotchas
CI⚡️CD
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5764414/Screenshot_2019-02-09_at_15.02.27.png)
Naming
clashes
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5813851/Screenshot_2019-02-22_at_18.35.41.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5813851/Screenshot_2019-02-22_at_18.35.41.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5764430/Screenshot_2019-02-09_at_15.08.09.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/266886/images/5764430/Screenshot_2019-02-09_at_15.08.09.png)
/offline
/-/offline
Request
types
self.addEventListener('fetch', ({request}) => {
if (
request.method === 'GET' &&
request.mode === 'navigate'
) {
event.respondWith(
fetch(request).catch(() => (
caches.match('/-/offline')
))
)
}})
Fetch
./service_worker.js
CI⚡️CD
CI⚡️CD
✅ Basic PWA Support
✅ Vulnerability Caching
June 22nd 2019
v12.0
Thanks!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/samdbeckham/images/1254654/me.jpeg)
Sam Beckham
@samdbeckham
Making GitLab a PWA
By Sam Beckham
Making GitLab a PWA
- 1,275