$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Progressive Web Apps with React
Search
Jonathan Mills
October 10, 2018
Technology
0
41
Progressive Web Apps with React
Jonathan Mills
October 10, 2018
Tweet
Share
More Decks by Jonathan Mills
See All by Jonathan Mills
Javascript’s scary side
jonathanfmills
0
470
Keeping Up
jonathanfmills
1
220
Other Decks in Technology
See All in Technology
Gemini でコードレビュー知見を見える化
zozotech
PRO
1
260
ログ管理の新たな可能性?CloudWatchの新機能をご紹介
ikumi_ono
1
750
会社紹介資料 / Sansan Company Profile
sansan33
PRO
11
390k
意外とあった SQL Server 関連アップデート + Database Savings Plans
stknohg
PRO
0
320
Jakarta Agentic AI Specification - Status and Future
reza_rahman
0
100
AIの長期記憶と短期記憶の違いについてAgentCoreを例に深掘ってみた
yakumo
3
260
エンジニアリングマネージャー はじめての目標設定と評価
halkt
0
280
モダンデータスタック (MDS) の話とデータ分析が起こすビジネス変革
sutotakeshi
0
500
re:Invent2025 3つの Frontier Agents を紹介 / introducing-3-frontier-agents
tomoki10
0
120
乗りこなせAI駆動開発の波
eltociear
1
1.1k
Challenging Hardware Contests with Zephyr and Lessons Learned
iotengineer22
0
210
Sansanが実践する Platform EngineeringとSREの協創
sansantech
PRO
2
860
Featured
See All Featured
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
390
Making Projects Easy
brettharned
120
6.5k
Optimizing for Happiness
mojombo
379
70k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
A Modern Web Designer's Workflow
chriscoyier
698
190k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
9.8k
Visualization
eitanlees
150
16k
Navigating Team Friction
lara
191
16k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
48
9.8k
Side Projects
sachag
455
43k
[SF Ruby Conf 2025] Rails X
palkan
0
520
Transcript
@ j o n a t h a n f
m i l l s #reactPWA P R O G R E S S I V E W E B A P P S W I T H R E A C T
None
None
None
Progressive Web Apps to the rescue!
What is a PWA? RELIABLE
What is a PWA? RELIABLE FAST
What is a PWA? RELIABLE FAST ENGAGING
Demo
None
Service Workers
Have you heard of a Web Worker?
ServiceWorker != WebWorker
None
if('serviceWorker' in navigator) { navigator.serviceWorker.register('/offline.js', { scope: '/' }) .then(function(registration)
{ console.log('Service Worker Registered'); }); navigator.serviceWorker.ready .then(function(registration) { console.log('Service Worker Ready'); }); } SERVICE WORKER
INSTALL const CACHE_NAME = ‘DevUp’ // Version 0.6.5 self.addEventListener('install', e
=> { console.log('installing service worker!!') const timeStamp = Date.now(); e.waitUntil( caches.open(CACHE_NAME).then(cache => { return cache.addAll([ `/`, `/index.html`, `/json/data.json`, `/static/js/bundle.js` ]) .then(() => self.skipWaiting()); }) ); });
INSTALL const CACHE_NAME = ‘DevUp’ // Version 0.6.5 self.addEventListener('install', e
=> { console.log('installing service worker!!') const timeStamp = Date.now(); e.waitUntil( caches.open(CACHE_NAME).then(cache => { return cache.addAll([ `/`, `/index.html`, `/json/data.json`, `/static/js/bundle.js` ]) .then(() => self.skipWaiting()); }) ); });
INSTALL const CACHE_NAME = ‘DevUp' // Version 0.6.5 self.addEventListener('install', e
=> { console.log('installing service worker!!') const timeStamp = Date.now(); e.waitUntil( caches.open(CACHE_NAME).then(cache => { return cache.addAll([ `/`, `/index.html`, `/json/data.json`, `/static/js/bundle.js` ]) .then(() => self.skipWaiting()); }) ); });
ACTIVATE self.addEventListener('activate', event => { console.log('activating service worker'); event.waitUntil(self.clients.claim()); });
FETCH self.addEventListener('fetch', function(event) {…})
FETCH event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit - return
response if (response) { return response; } var fetchRequest = event.request.clone(); return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic'){ return response; } var responseToCache = response.clone(); caches.open(CACHE_NAME)
FETCH event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit - return
response if (response) { return response; } var fetchRequest = event.request.clone(); return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic'){ return response; } var responseToCache = response.clone(); caches.open(CACHE_NAME)
FETCH event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit - return
response if (response) { return response; } var fetchRequest = event.request.clone(); return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic'){ return response; } var responseToCache = response.clone(); caches.open(CACHE_NAME)
event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit - return response
if (response) { return response; } var fetchRequest = event.request.clone(); return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic'){ return response; } var responseToCache = response.clone(); caches.open(CACHE_NAME) FETCH
} var fetchRequest = event.request.clone(); return fetch(fetchRequest).then( function(response) { //
Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic'){ return response; } var responseToCache = response.clone(); caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); }); return response; } ); }) ); FETCH
} var fetchRequest = event.request.clone(); return fetch(fetchRequest).then( function(response) { //
Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic'){ return response; } var responseToCache = response.clone(); caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); }); return response; } ); }) ); FETCH
} var fetchRequest = event.request.clone(); return fetch(fetchRequest).then( function(response) { //
Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic'){ return response; } var responseToCache = response.clone(); caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); }); return response; } ); }) ); FETCH
} var fetchRequest = event.request.clone(); return fetch(fetchRequest).then( function(response) { //
Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic'){ return response; } var responseToCache = response.clone(); caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); }); return response; } ); }) ); FETCH
Demo
None
Manifest
A manifest describes your web page in an
INDEX.HTML <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
MANIFEST { "short_name": "Library", "name": “DevUp Library Demo", "icons": [
{ "src": "book.png", "sizes": "192X192", "type": "image/png" } ], "start_url": "./index.html", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" }
Demo
None
Let’s Deploy
None
Github Pages to the rescue
PACKAGE.JSON – ADDING GH-PAGES { "name": "library", "version": "0.1.0", "private":
true, "homepage": “https://jonathanfmills.github.io/DEVUPPWA”, "dependencies": { "gh-pages": "^1.1.0", … }, "scripts": { "predeploy": "npm run build", "deploy": "gh-pages -d build", } }
Demo
http://bit.ly/devuppwa
@ j o n a t h a n f
m i l l s #reactPWA P R O G R E S S I V E W E B A P P S W I T H R E A C T