Transform a React App into a Progressive Web App (PWA)

Transform a React App into a Progressive Web App (PWA)

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.

No alt text provided for this image

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:

No alt text provided for this image

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:

No alt text provided for this image

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”):

No alt text provided for this image

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:

No alt text provided for this image

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!


Gurucharan Chouhan

SDE @Payoneer ?? | React Js | Redux | GraphQl | Typescript

1 年

Great Article :

回复
Emanuel Romano

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! ??

回复
Noor Ul Amin

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?

回复
Prasanth Ramanathan

Full Stack Developer || JavaScript ||Angular || React - Redux || Material UI || NodeJS ||Go-Lang || CloudFoundry || Concourse

2 年

Good article to bootstrap PWA.,

回复
Asel Peiris

Experienced Full Stack Developer | AWS Enthusiast

4 年

Great read Shankhadeep!

要查看或添加评论,请登录

社区洞察

其他会员也浏览了