Earlier this week I wrote about some experiments I was doing with Service Workers in ASP.NET Core. This is an update to that.

So, what is a Progressive Web App (PWA)?

A Progressive Web App uses modern web capabilities to deliver an app-like user experience – Progressive Web Apps

The benefits of PWAs are many. My personal favorites include:

  • They are faster than regular websites
  • They are more reliable
  • They work offline
  • They can be installed on the desktop or phone
  • Most major browsers already support them (Safari and Edge coming soon)

Any website or web application can add the capabilities that turns them into PWAs. The capabilities to add are:

  1. The website must be served over HTTPS
  2. Add a Web App Manifest (it’s a simple JSON file)
  3. Add a Service Worker (a JavaScript file)

With ASP.NET Core we can automate a lot of this to make it easier and more integrated with the rest of the application.

Getting started

So, let’s get started turning our ASP.NET Core web application into a full fledged PWA by following a few easy steps. This will make your site work offline, be faster and installable by supporting browsers.

Step 1 – install a NuGet package

Install the NuGet package WebEssentials.AspNetCore.PWA into your ASP.NET Core project.

Step 2 – add a manifest and icons

Add a file called manifest.json in the wwwroot folder as well as 2 image icons.


You can have as many image icons as you want as long as you have one in the size of 192x192 and one 512x512 pixels.

Fill in the manifest.json file similar to this:

  "name": "Awesome Application",
  "short_name": "Awesome",
  "description": "The most awesome application in the world",
  "icons": [
      "src": "/img/icon192x192.png",
      "sizes": "192x192"
      "src": "/img/icon512x512.png",
      "sizes": "512x512"
  "display": "standalone",
  "start_url": "/"

Step 3 – register a service

Inside the ConfigureServices method in Startup.cs, add a call to services.AddProgressiveWebApp() like so:

public void ConfigureServices(IServiceCollection services)

Voila! The app is now a full blown PWA.

To verify it works and your web app now behaves like a PWA in supported browsers, check out the verification step on the project readme.

Also see how it is implemented in the Miniblog.Core source code which is a production web app. This very website (madskristensen.net) is also running it.


There are plenty of ways to configure the behavior of the Web App Manifest as well as the service worker. Read the documentation for more info.

I do want to call out that the NuGet package creates a strongly typed object (WebEssentials.AspNetCore.Pwa.WebManifest) out of the manifest.json file and makes it available in the dependency injection system. That way you can have a single source of truth for meta data properties such as application name, description and icon list.

Next steps

Read the full description of using the NuGet package where you’ll also find links to the various specifications and videos about PWAs. 


If this is of interest to you and you’d like to contribute to the project on GitHub then you are more than welcome to do so. Open bugs, suggest features and send pull requests are all appreciated.


Yushell Darwich

I have the latest .NET Core 2.0.3. Created a Web Application without auth. Added the NuGet, the manifest, the PNGs. Enabled SSL. And I'm getting the following error under the Application tab/ section Service Workers in chrome tools: (unknown) #1: ServiceWorker failed to install: ServiceWorker failed to handle event (event.waitUntilPromise rejected) Uncaught (in promise) TypeError: Cannot read property 'addAll' of undefined Any ideas on what's wrong?

Yushell Darwich

Mads Kristensen

@Yushell, It sounds like something is wrong with the service worker JavaScript. Could you open an issue on github and paste the code from the service worker (localhost:1234/serviceworker) as well as any customizations you've done in the registration in startup.cs?

Mads Kristensen

Judah Gabriel Himango

Mads, Awesome library! I tried out PWAs a couple months ago but abandoned it, feeling like the tooling and libraries were too immature. Here you come along and make it easy - thanks! Two questions: 1. It's not clear to me how RoutesToPrecache interacts with Strategy. If Strategy is set to the default (cache HTML + assets with ?v query params), do I still get caching for any assets I specify in RoutesToPrecache? 2. How do I invalidate cache items? (Like, I won't ever need foo.html?v=oldversion, but there it is in the service worker cache.) While it's not a huge issue, I am concerned that if I never clear out old stuff out of the cache, users's browsers will start filling up with a million variations on foo.html

Judah Gabriel Himango

Mads Kristensen

@Judah, 1. RoutesToPreCache is a comma separated string containing routes that you want the service worker to put in the cache when the service worker is installed. By default it caches only "/offline.html" which the NuGet package is serving. You can add anything that makes sense to your app - usually the resources that makes up the shell of your site (stylesheets, js, chrome images etc.) 2. You do that by changing the name of the "CacheId" property in PwaOptions. That will clear the cache and use the new one until you change it again. It would be cool to add a mechanism that removes items from cache following certain heuristics, but I haven't gotten to that yet. Any help on that would be appreciated.

Mads Kristensen

Judah Gabriel Himango

Thanks, that helps. As for clearing out the cache, yeah I'm interested in that. In my scenarios, a file of the same name but different version query param should remove the old version from the cache. That is, foo.html?v=1 should be deleted when foo.html?v=2 is added. I'm certainly willing to contribute such a feature. Is my scenario general enough to contribute as a PR? Or did you have a different idea for cache invalidation in mind?

Judah Gabriel Himango

Comments are closed