November 20, 2019
  • All
  • Engineering
  • Perspectives
  • Tutorials
  • Ionic
  • Progressive Web Apps
  • PWA
  • react
  • service workers

Progressive Web Apps in Ionic React

Mike Hartington

Director of Developer Relation...

With the launch of Ionic React a few weeks back, the reception from the community has been incredible. We’re thrilled that so many of you are excited to use Ionic React, and I myself was excited to try React a bit more. Given that I spend most of my time with Angular, it was fun to see what another framework could offer as I learned how to “think” in React.

With that in mind, I wanted to share my experience rebuilding a personal demo app that I have built in Angular (Star Track) and rebuild it in React. For this exercise, I’m going to focus on how I created a Progressive Web App (or PWA) with Ionic React. Let’s dive in!

If you would like to see the final results, you can see an early version of Star Track React here.

App Manifest

Adding the App Manifest was fairly straightforward.

If you’ve built with Angular, you’re probably used to running ng add @angular/pwa and having one created for you. The logic here is that not everyone may need a PWA.

With Create React App (CRA), however, a manifest and placeholder icons are provided out-of-the-box. This was nice, as I could quickly jump in there, make the edits I needed, and get back to building my app.

Service Workers

Service Workers are an essential part of the PWA experience. Without one, our apps would not be able to work offline or be resilient to flaky network connections.

In Angular, we work with the @angular/service-worker package to create and interact with our Service Worker. This allows us to express our caching strategy for our app’s resources in a JSON files.

So how does this work in React?

Out-of-the-box, Create React App (CRA) utilized the Workbox library from the Chrome team. By using Workbox’s webpack plugin, we’re able to get a complete list of all the resources our app needs and create a precache with a hash revision. This is done automatically at build time so there’s no need for us to configure this.

But we are putting the pig before the pen here, as we need to opt-in to Service Workers in our main index.ts file. By default CRA has Service Workers disabled due to their advanced nature. The idea being that devs are not used to their content caching. While I disagree with this, I do appreciate that the team has noted their reason on the CRA docs.

To opt-in and register our Service Worker, we can call serviceWorker.register():

//index.ts

import React from 'react';
import ReactDOM from 'react-dom';
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.unregister();
serviceWorker.register();

With this call, we’re able to register the Service Worker that the build scripts will create for us.

Handling updates

Since CRA internalizes a lot of moving pieces with Service Workers, I was worried that I would not have a mechanism for handling updates. But digging into the registration function, there’s an optional config parameter that could be passed that could handle this. The config argument is an object that can accept an onSuccess and onUpdate key, with a callback function as their values.

From this, we can modify our original register call to pass a config object:

register({
  onUpdate: async (registration: ServiceWorkerRegistration) => {
    await registration.update();
  }
});

Not a whole lot of magic happening here, but it’s a bit clearer when looking at the registration function

if (navigator.serviceWorker.controller) {
  // At this point, the updated precached content has been fetched,
  // but the previous service worker will still serve the older
  // content until all client tabs are closed.
  console.log( 'New content is available and will be used when all tabs for this page are closed. See https://bit.ly/CRA-PWA.');

  // Execute callback
  if (config && config.onUpdate) {
    config.onUpdate(registration);
  }
}

When our Service Worker has registered, we can hook into the lifecycles and trigger an update when the new content has been cached.

Parting Thoughts

While focusing mainly on Service Worker and App Manifest, I’m still diving deep into PWAs with React. With what I (and the rest of the Ionic team) learn while we build more and more Ionic React apps, we’ll be able to provide the best suggestions for delivering fast, powerful, feature-rich apps with React. Cheers 🍻!


Mike Hartington

Director of Developer Relation...