PWA & DX Part 2: The Ingredients

The Components of a PWA

In part 1 of this series, we have learned what a PWA is and especially what not. We learned about the benefits and how it makes sense in the field of Content Management and Digital Experiences. This part gets a little more technical and discusses the components and aspects that turn a web application (or even a simple website) into a PWA.

Nowadays, we can assume responsive web design and serve via HTTPS as standard.

That means we only have to add the following components to our existing app:

  • Service Worker: JavaScript files that run in the background, allowing the PWA to work offline and receive push notifications.
  • Web App Manifest: A JSON file that provides information about the PWA, such as the name, description, and icons.
  • A set of icons that a referenced within the Web App Manifest.

The following code shows an example of a simple and minimal Service Worker:

// React on the install event if the app gets installed and "install" shell.
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('my-cache').then(function(cache) {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles.css',
        '/script.js'
      ]);
    })
  );
});

// React on the fetch event e.g. for new files and serve the files from out the cach if available.
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      return response || fetch(event.request);
    })
  );
});

This Service Worker caches the app shell (HTML, CSS, and JS files that are building the foundation to be installed and used offline) when it’s installed and serves the cached files when the user is offline. The ‘fetch’ event listener intercepts network requests and checks if a cached response is available before making a network request.

Here’s an example of a minimal Web App Manifest:

{
  "name": "My PWA",
  "short_name": "MyPWA",
  "description": "A simple PWA",
  "icons": [
    {
      "src": "/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start_url": "/",
  "display": "standalone",
  "background_color": "#fff",
  "theme_color": "#000"
}

This manifest sets up basic information about the PWA, including the name, short name, description, icons, start URL, display mode, and colors.

The following figure shows a minimal but typical directory structure of a PWA. Depending on the use case, this will have more files; some files might be located differently.

my-pwa/
├── index.html
├── styles.css
├── script.js
├── icons/
│   ├── icon-72x72.png
│   ├── icon-96x96.png
│   ├── icon-128x128.png
│   ├── icon-144x144.png
│   ├── icon-152x152.png
│   ├── icon-192x192.png
│   ├── icon-384x384.png
│   └── icon-512x512.png
├── manifest.json
├── sw.js
  • index.html: The main HTML file for the existing web application.
  • styles.css: The CSS file that styles the exisiting web application.
  • script.js: The JavaScript file that contains the logic of the PWA (and the exisiting web application), like for registering the Service Worker.
  • icons/: The directory that contains the icons of the PWA.
  • manifest.json: The Web App Manifest file that contains the metadata of the PWA.
  • sw.js: The Service Worker JavaScript file that enables offline functionality.

The Service Worker JavaScript file is typically placed in the root directory of a PWA so that it can have access to the entire application’s files and resources. This example of a Service Worker intercepts network requests and caches files. It needs access to all necessary files to do its job effectively. If the Service Worker were placed in a subdirectory, it would only have access to the files in that subdirectory.

// Verify that the browser has PWA capabilities. If so register the Service Worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/sw.js').then(function(registration) {
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

The code above is an example of how the script.js file of a basic PWA may look like. It registers a Service Worker if the browser supports it.

Next we have a look into the index.html and what needs to be added.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>My PWA</title>
    <!-- Referencing the manifest -->
    <link rel="manifest" href="/manifest.json">
    <link rel="stylesheet" href="/styles.css">
  </head>
  <body>
    <h1>Welcome to My PWA</h1>
    <p>This is a simple PWA.</p>
    <!-- The script that - in this example case - registers the Service Worker -->
    <script src="/script.js"></script>
  </body>
</html>

In this simple example, we only need to reference the manifest and the script that registers the Service Worker.

In this blog post, we learned about the components that must be added to web applications to turn them into PWAs. The Service Worker installs the shell of the app and intercepts the fetch process for requests to the server. We also looked at the directory structure of a simple but typical PWA. We learned that the Service Worker should be placed in the root folder to have access to all files.

In part 3, we will apply this knowledge to a static website to see a practical real live example; we will turn TheDXPilot.io into a PWA!

If you want to read more about software architecture, PWA, DX, and CM, please follow my social media for updates.

Markus Nordhaus
Markus Nordhaus
Senior Solutions Architect

Markus Nordhaus is a Computer Scientist and Pilot who is bringing Computer Science, Software Engineering and Aviation together to awesome Digital Experiences