DEV Community

Cover image for #07 - Developing Progressive Web Apps
Nitya Narasimhan, Ph.D for Microsoft Azure

Posted on • Originally published at

#07 - Developing Progressive Web Apps

Welcome to Day 7 of #30DaysOfPWA! New to the series? Three things you can do to catch up:

This is a shortened version of this canonical post for the #30DaysOfPWA.

Let's Recap

Actually, let's take a minute to celebrate this moment. We made it to the end of week 1 and it was packed with a quick tour through Progressive Web App core concepts! Here's a visual guide to what we covered!

Visual Guide to Core Concepts Week!


What We'll Do Today

It's time to put all those learnings to work with a practical code exercise. Let's take an existing application and iteratively enhance it for Progressive Web App functionality. Here's a roadmap:

  • Select an existing web app. Something that works!
  • Run app locally, inspect it for PWA baseline.
  • Make it safe. Deploy to an HTTPS endpoint.
  • Make it installable. Add that Web App Manifest.
  • Make it network-independent. Register a service worker.
  • Make it work offline. Try a cache-first policy.
  • Audit the PWA. Does it meet recommended practices?
  • Test the PWA. Does it install? Work offline?

Select a Web App

If you follow me online, you might have noticed my #Recipes4AJ tweets. It's my way of leaving recipe trails for my 13yo should he find himself wanting to make them some day. And I wanted to convert these into a website with more images, tags and search capability. I found this great Eleventy starter for an Online Cookbook that was a perfect starting point.

But I realized that we might want to use recipes on camping trips (when we are off the grid) or when traveling (with limited data). Wouldn't it be great if this online cookbook was offline friendly? Let's make our cookbook a Progressive Web App!

Audit as Baseline

My first step was to create an instance of this template and make sure the current app worked on my local device following the basic npm install, npm run dev guidance provided by the starter template.

Now, I could inspect it in my browser DevTools. Luckily for us, Microsoft Edge and other browsers will allow PWA APIs to be accessed from http://localhost for debugging purposes only. We don't have a PWA yet, but this will come in handy as we iterate.

My Application tab shows there is nothing to see (no Manifest, no Service Workers) but I can run a Lighthouse audit to get a sense of Baseline performance and PWA readiness. Here is what I see.

Secure it with HTTPS

The next step was to take my local app and deploy it to an HTTPS-enabled server so I can satisfy one core requirement of PWA: make it secure!

I chose to deploy my app to Azure Static Web Apps. The process was quick and painless using the Azure Static Web Apps Extension for VS Code. Simply follow the instructions on that page and you should have your app deployed to the Azure cloud in no time.

Some benefits with this are that the workflow asks you to authenticate with GitHub, and then automatically provisions your repo with GitHub Actions to make the deployment automatic on further commits.

We now have our app running in the cloud - a quick look at the Lighthouse audit shows that production deployment gives us a positive impact. Look at all those greens! But we still have to fix PWA issues.

Performance PWA
Lighthouse audit of secure web app. Lighthouse audit of secure web app PWA.

What's Next for PWA?

We know we have to add a manifest, and configure the service worker. We can do this manually by:

  • Creating a manifest.json file with relevant members and adding the relevant <link> in app HTML to show its location.
  • Creating a sw.js file for service worker implementation and populating it with the relevant lifecycle and functional event handlers for operation, and placing it in the right location of app structure to suit its scope.
  • Registering the service worker in the app code.

We can take advantage of helpful PWA tools that make this easier for us.

For example, Eleventy (the static site generator powering my app) provides an eleventy-plugin-pwa that uses Workbox under the hood. We'll look at Workbox and other tools in our "Developer Tools" week. But for now, I wanted to be a little more hands-on so I could learn what was happening.

The PWABuilder Assist

I started by using PWABuilder - a free auditing tool that evaluates your PWA, providing an audit report with actionable options to help you fix identified issues. Just enter your hosted app URL and click Start. We will walk you through PWABuilder in details in week 3.

Here's what my audit report looks like. Sad Trombone - we only scored a 30!

Audit result from PWABuilder.

The good news? We checked off the Security requirement with our HTTPS-enabled hosting. More good news? The Manifest Options and Service Worker Options tabs can help us generate the required manifest.json and sw.js files in a way that helps us understand what exactly we are adding, and why.

Configure Manifest Options

Let's dive into the Manifest Options tab. Here is what that looks like as I start modifying things.

Manifest options tab in PWABuilder.

Some things that make this useful:

  • It pre-populates some fields, making it easier to do small edits.
  • It provides previews showing how manifest options impacts your app on desktop and mobile.
  • It has helpers to generate icons and screenshots!

You can generate and save the file to manifest.json for use in your PWA. You can also just generate this with any text editor. Just make sure it is valid JSON. I saved the file and manually cleaned it up a little:

  • updating icon paths to reflect project structure
  • picking some categories for my app domain

The icons and manifest were saved into the src/ and src/assets folder of my PWA and the eleventy.js config file updated to show that these should be "passed through" to the build as is. Here is my manifest.json!

    "lang": "en-us",
    "name": "AJ's Offline Cookbook",
    "short_name": "AJ's Cookbook",
    "description": "An offline-friendly online cookbook perfect for that campfire trip off-the-grid",
    "start_url": "/",
    "background_color": "#ff001b",
    "theme_color": "#e8ca6c",
    "orientation": "any",
    "display": "standalone",
    "scope": "/",
    "dir": "ltr",
    "icons": [
            "src": "/assets/logo192.png",
            "sizes": "192x192",
            "type": "image/png"
            "src": "/assets/logo512.png",
            "sizes": "512x512",
            "type": "image/png"
    "categories": [
    "screenshots": [],
    "shortcuts": []
Enter fullscreen mode Exit fullscreen mode

Now, we need to update the app HTML to show where the manifest.json is located. In my 11ty project, I update this in the src/layouts/base.njk template. The end result is a <link> in your app index.html that looks like this.

<link rel="manifest" href="/manifest.json">
Enter fullscreen mode Exit fullscreen mode

If you inspect a site preview (localhost) or deployed (build) version now, you should see the Manifest section of the Applications tab reflect the properties you just added. Let's commit the changes and go to the next fix.

Add Service Worker

As before, you can do this manually or jumpstart the process using PWABuilder. We need to do two things:

  • Create the service worker implementation.
  • Register the service worker to initialize on startup

This guide explains how we can do this with PWABuilder. Let's try it out.

I generated the service worker for the cache-first strategy. The download gave me two files.

First the pwabuilder-sw-register.js file which does the registration for you. The contents are below.

// This is the "serving cached media" service worker

// Add this below content to your HTML page inside a <script type="module"></script> tag, or add the js file to your page at the very top to register service worker
// If you get an error about not being able to import, double check that you have type="module" on your <script /> tag

 This code uses the pwa-update web component to register your service worker,
 tell the user when there is an update available and let the user know when your PWA is ready to use offline.

import '';

const el = document.createElement('pwa-update');
Enter fullscreen mode Exit fullscreen mode

I saved this to src/pwbuilder-sw-register.js in my project repo, then added this to my src/layouts/base.njk file inside the <head> tag.

Next, the pwabuilder-sw.js implementation file.

// This is the service worker with the Cache-first network

const CACHE = "pwabuilder-precache";


self.addEventListener("message", (event) => {
  if ( && === "SKIP_WAITING") {

  new RegExp('/*'),
  new workbox.strategies.CacheFirst({
    cacheName: CACHE
Enter fullscreen mode Exit fullscreen mode

Lastly, I updated the eleventy.js configuration to have both files were passed through to build. Let's do a quick inspection in DevTools. Would you look at that? We have a service worker!! Time to commit the changes and push them to deployed app.

The Final Audit

Let's run that final PWA Builder Audit!

Where It Started Where It's Going
PWABuilder audit when project started. PWABuilder audit now.

We did it!! We have an installable PWA with a viable offline experience, ready to start building up from!! You can see my demo site running on Azure Static Web Apps here. Note that this has been minimally customized from the template. I'll do more with it after this week!

Want to see the code? Check out this repository to see the changes made in each step of this process. Caveat: I plan to continue working with this to make more improvements.

For instance:

  • adding an offline page
  • trying other service worker strategies
  • adding screenshots and improving the manifest
  • exploring new web capabilities
  • adding actual content (my own)

The SETUP page should document these changes as I go, in case you want to follow along!

The Final Proof

Yes, the audit tells us we did good - but does it actually work the way we want it to? Time to test it for PWA capabilities!!

  • Install it on desktop. This is what mine looks like when installed on a MacBook Pro. Notice the standalone mode (no browser address bar or other hints that this is a web app)
  • Explore it - does it work and feel like a native app?
  • Go offline!. Disconnect from the network (e.g., put your laptop in Flight mode). Can you still launch and explore it?
  • Does it feel native? Use the search tool (Finder on Mac) - can you discover your PWA just like any other natively installed app?

PWA is now installed.

I had a wonderful time being your guide through week 1 of core concepts. Now if you'll excuse me, I have a long weekend ahead. I think I might need to plan that camping trip, go off the grid - and cook up a storm!

Happy learning trails!


Your turn!! Take an existing app and try to refactor it to be a basic PWA.

  • Inspect it locally - check your Application and Lighthouse tabs.
  • Audit it on PWABuilder - get baseline scores and guidance
  • Host it on HTTPS-enabled server - GitHub pages or Azure Static Web Apps can help.
  • Add a Manifest - and link it.
  • Test it - can you install this to desktop?
  • Add a Service Worker - and register it.
  • Inspect it once in DevTools locally - then deploy it.
  • Test it - go Offline. Does the app still work?
  • Audit it - how has the score changed?

Coming Next: Advanced Capabilities!

We have just begun the journey but hopefully you now have a solid foundation for an app that you can play with and evolve as we walk through more advanced capabilities, developer tools, platforms and practices.

Here's a sneak peek at what's in store for Week 2 - get ready to dive into advanced capabilities that can take your PWA experiences to the next level!

Visual Guide for Week 2

Want to read more content from Microsoft technologists? Don't forget to follow Azure right here on

Top comments (0)