Transform a React App into a Progressive Web App (PWA)
Shankhadeep Bhadra
Engineering @ ServiceNow || Ex Dassault Systèmes || All about JavaScript and Tech
What are Progressive Web Apps (PWAs)?
Reliable — Service workers (client-side proxies that pre-cache key resources) enable PWAs to load instantly and provide an instant, reliable experience for users, regardless of the network state.
Fast — 53% of users will abandon a site if it takes longer than 3 seconds to load! And once loaded, users expect them to be fast.
Engaging — PWAs are installable and can live on users’ home screens without the need for an app store. You can specify home screen icons and the page to load when the app is launched.
In this guide, you will:
- Set up a simple React app
- Learn how to use the Audits tab in Chrome Dev Tools to run audits for your React app against Google’s PWA Checklist.
- Register a Service Worker
- Apply the the principle of progressive enhancement to your React app
- Add a splash icon
- Deploy your React PWA
Step 1: Set Up a Simple React App
Create a new “Task Manager” React app with create-react-app, which will create a new boilerplate React app for you!
To do this, run the following commands:
npm install -g create-react-app create-react-app pwa-task-manager
Next, install react-router-dom, which will allow you to navigate between components in your React app
cd pwa-task-manager npm install --save react-router-dom
To create a simple page with navigation, replace the code in src/App.js with the following:
import React, { Component } from 'react'; import { BrowserRouter, Route, Link } from 'react-router-dom'; import './App.css'; const NavBar = () => ( <div className="navbar"> <h3>Task Manager</h3> <Link to="/">Current Tasks</Link> <Link to="/completed">Completed Tasks</Link> </div> ); const Template = (props) => ( <div> <NavBar /> <p className="page-info"> {props.title}: </p> <ul className={props.status}> <li>Task 1</li> <li>Task 2</li> <li>Task 3</li> </ul> </div> ); const CurrentTasks = () => ( <Template title="Current Tasks" status="Current"/> ); const CompletedTasks = () => ( <Template title="Completed Tasks" status="Completed"/> ); class App extends Component { render() { return ( <BrowserRouter> <div> <Route exact path="/" component={CurrentTasks}/> <Route path="/completed" component={CompletedTasks}/> </div> </BrowserRouter> ); } }
export default App;
To update the default styles, replace the code in src/App.css with the following:
.navbar { background-color: #4169e1; } .navbar h3 { display: inline-block; text-align: left; padding: 10px; color: black; text-decoration: none; } .navbar a { display: inline-block; padding: 10px; color: #fff; text-decoration: none; } .page-info { padding: 10px; } .Current { color: #2e8b57; } .Completed { color: #ff6347; text-decoration: line-through;
}
You just set up a simple React app with two routes! To test your app in the browser, run the following command:
npm-start
Let’s start converting it into a PWA!
Step 2: Use the Audits Tab in Chrome Dev Tools
In this step, we’ll use the Audits tab to run audits for your React app against Google’s PWA Checklist. The Audits tab allows you to check how your app is doing in certain against Google’s set audits for things like performance, accessibility, progressive web apps, etc.
Open up Chrome Dev Tools in your browser and toggle to the Audits tab. Check the “Progressive Web App” box next to Audits.
Once you click “Run audits”, an automated report is generated through Lighthouse (an open-source, automated tool for improving the quality of web pages).
The generated report should look like this:
Let’s fix those failed audits!
Step 3: Register a Service Worker
What is a service worker?
Service workers (client-side proxies that pre-cache key resources) enable PWAs to load instantly and provide an instant, reliable experience for users, regardless of the network state.
Create a new worker.js file in the public folder (public/worker.js) and add the following code:
var CACHE_NAME = 'pwa-task-manager'; var urlsToCache = [ '/', '/completed' ]; // Install a service worker self.addEventListener('install', event => { // Perform install steps event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { console.log('Opened cache'); return cache.addAll(urlsToCache); }) ); }); // Cache and return requests self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit - return response if (response) { return response; } return fetch(event.request); } ) ); }); // Update a service worker self.addEventListener('activate', event => { var cacheWhitelist = ['pwa-task-manager']; event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.map(cacheName => { if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName); } }) ); }) );
});
Update your index.html file in the public folder (public/index.html) to check if the client’s browser supports service workers :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <meta name="theme-color" content="#000000" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <title>React App</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <script> if ('serviceWorker' in navigator) { window.addEventListener('load', function() { navigator.serviceWorker.register('worker.js').then(function(registration) { console.log('Worker registration successful', registration.scope); }, function(err) { console.log('Worker registration failed', err); }).catch(function(err) { console.log(err); }); }); } else { console.log('Service Worker is not supported by browser.'); } </script> </body> </html>
Now, update index.js in the src folder (src/index.js) from service-worker.unregister() to serviceWorker.register().
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; ReactDOM.render(<App />, document.getElementById('root')); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.register();
Restart (npm start) and reload your React app — you should see the message “Worker registration successful” in the console.
Go back to the Audits tab in Chrome Dev Tools and run a new report. It should look something like this:
Your React app now:
- Responds with a 200 when offline
- Registers a service worker
Progress!
Step 4: Apply the Principle of “Progressive Enhancement”
What is progressive enhancement?
Progressive enhancement is a strategy that emphasizes core webpage content first and progressively adds more layers and features on top of the content as the end-user’s network connection allow.
In order to make sure your React app renders styling and works without any JavaScript loaded, add lines 13–45 and 50–57 (to your index.html file in the public folder (public/index.html).
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <meta name="theme-color" content="#000000" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <title>React App</title> <style type="text/css"> body { margin: 0; padding: 0; font-family: sans-serif; } .navbar { background-color: #4169e1; } .navbar h3 { display: inline-block; text-align: left; padding: 10px; color: black; text-decoration: none; } .navbar a { display: inline-block; padding: 10px; color: #fff; text-decoration: none; } .page-info { padding: 10px; } .Current { color: #2e8b57; } .Completed { color: #ff6347; text-decoration: line-through; } </style> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"> <div class="navbar"> <h3>Task Manager</h3> <a href="/">Current Tasks</a> <a href="/completed">Completed Tasks</a> </div> <p class="page-info"> Loading... </p> </div> <script> if ('serviceWorker' in navigator) { window.addEventListener('load', function() { navigator.serviceWorker.register('worker.js').then(function(registration) { console.log('Worker registration successful', registration.scope); }, function(err) { console.log('Worker registration failed', err); }).catch(function(err) { console.log(err); }); }); } else { console.log('Service Worker is not supported by browser.'); } </script> </body> </html>
Try reloading your React app without internet connection — the app should contain the added content (above) when JavaScript is not available.
Step 5: Add a Splash Icon
What are splash icons?
You can define a set of icons for the browser to use. These icons are used in places like the home screen, app launcher, task switcher, splash screen, etc.
In order to pass the audit, you must include a 512x512 pixel icon. Chrome will automatically scale the icon for the device.
To do this, first create a new images folder in the public folder (public/images). Add the following icon image to the folder (and re-name it “icons-512.png”):
Next, update the “icons” array in the manifest.json file in the public folder (public/manifest.json) to match the following:
{ "short_name": "React App", "name": "Create React App Sample", "icons": [ { "src": "/images/icons-512.png", "type": "image/png", "sizes": "512x512" } ], "start_url": ".", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" }
Restart (npm start) and reload your React app.
Go back to the Audits tab in Chrome Dev Tools and run a new report. It should look something like this:
Your React app is now configured for a custom splash screen.
Almost there!
Step 6: Deploy Your React PWA
The final step is to redirect HTTP traffic to HTTPS. Why? To make your app secure. In most cases, deploying your app will take care of this step for you.
NOTE: You should have already connected your repository to GitHub and committed your changes up to this point.
For some development platforms (like Firebase), your app will automatically become secure (redirect to HTTPS) once you deploy. For others (like Heroku), you’ll have to get a certificate from a Certificate Authority (CA) and upload it.
Once your app is deployed and secure, your last audit should be passing.
Congratulations, you just created a working progressive web app!
SDE @Payoneer ?? | React Js | Redux | GraphQl | Typescript
1 年Great Article :
Developer en CENSYS S.A. | Desarrollador Web
1 年Great article. Just had to search on the web for the serviceWorker script since my project didn't have one at first, but now my new PWA it's up and running! ??
Software Engineer @ Emumba | Full Stack Developer | Bachelor of Science in Computer Science
2 年Hey Shankhadeep Bhadra is it possible to make a react app PWA without deploying it?
Full Stack Developer || JavaScript ||Angular || React - Redux || Material UI || NodeJS ||Go-Lang || CloudFoundry || Concourse
2 年Good article to bootstrap PWA.,
Experienced Full Stack Developer | AWS Enthusiast
4 年Great read Shankhadeep!