Build A PWA With Webpack And Workbox — Smashing Magazine

This tutorial will assist you to rework an app that doesn’t work offline right into a PWA that works offline and exhibits an update out there icon. You’ll discover ways to precache belongings with workbox, deal with dynamic caching as well as deal with updates to your PWA. Comply with alongside and see how you can also apply these methods in your web site.

A Progressive Net App (PWA) is a website that makes use of trendy know-how to ship app-like experiences on the internet. It’s an umbrella time period for brand spanking new applied sciences such because the ‘web app manifest’, ‘service worker’, and extra. When joined collectively, these applied sciences help you ship quick and interesting consumer experiences together with your website.

This text is a step-by-step tutorial for including a service employee to an present one-page website. The service worker will permit you to make your web site work offline while additionally notifying your customers of updates to your website. Please word that that is based mostly on a small undertaking that is bundled with Webpack, so we’ll be utilizing the Workbox Webpack plugin (Workbox v4).

Using a software to generate your service employee is the beneficial strategy because it enables you to handle your cache efficiently. We can be utilizing Workbox — a set of libraries that make it straightforward to generate your service employee code — to generate our service employee in this tutorial.

Depending in your venture, you should use Workbox in three alternative ways:

  1. A command-line interface is on the market which lets you integrate workbox into any software you’ve got;
  2. A Node.js module is obtainable which lets you integrate workbox into any Node build device similar to gulp or grunt;
  3. A webpack plugin is on the market which lets you simply combine with a undertaking that’s constructed with Webpack.

Webpack is a module bundler. To simplify, you’ll be able to think of it as a device that manages your JavaScript dependencies. It permits you to import JavaScript code from libraries and bundles your JavaScript together into one or many information.

To get started, clone the next repository on your pc:

git clone [email protected]:jadjoubran/workbox-tutorial-v4.git
cd workbox-tutorial-v4
npm set up
npm run dev

Subsequent, navigate to http://localhost:8080/. It is best to be capable of see the foreign money app we’ll be utilizing all through this tutorial:

Screenshot of the foreign money app we’re building in this article.‘Currencies’ is a PWA that lists conversion fees of currencies towards the Euro (€) foreign money. (Giant preview)

Start With An App Shell

The appliance shell (or ‘app shell’) is a pattern impressed from Native Apps. It can help give your app a extra native look. It merely offers the app with a format and construction with none knowledge — a transitional display that’s aimed to improve the loading expertise of your net app.

Listed here are some examples of app shells from native apps:

Google Inbox App ShellGoogle Inbox App Shell: A few milliseconds before the emails load into the app shell. (Large preview)Twitter native App ShellTwitter’s native app on Android: App shell displaying navbar, tabs, and loader. (Giant preview)

And listed here are examples of app shells from PWAs:

App Shell of Twitter’s PWAThe app shell of Twitter’s PWA (Large preview)App Shell of Flipkart’s PWAThe app shell of Flipkart’s PWA (Giant preview)

Customers like the loading experience of app shells because they despise blank screens. A blank display makes the consumer feel that the web site is just not loading. It makes them really feel as if the web site have been caught.

App shells try to paint the navigational construction of the app as quickly as potential, such as the navbar, the tab bar as well as a loader signifying that the content material you’ve requested is being loaded.

So How Do You Build An App Shell?

The app shell pattern prioritizes the loading of the HTML, CSS and JavaScript that may render first. Because of this we need to give those assets full precedence, hence you must inline those belongings. So to build an app shell, you merely should inline the HTML, CSS and JavaScript which might be answerable for the app shell. In fact, you shouldn’t inline the whole lot however slightly keep within a restrict of round 30 to 40KB complete.

You’ll be able to see the inlined app shell in the index.html. You’ll be able to inspect the supply code by testing the index.html file, and you may preview it in the browser by deleting the

factor in dev instruments:

Currencies PWA App Shell with navbar & loaderThe App Shell that we’re building on this article. (Giant preview)

Does It Work Offline?

Let’s simulate going offline! Open DevTools, navigate to the community tab and tick the ‘Offline’ checkbox. Once you reload the page, you will notice that we’ll get the browser’s offline web page.

Browser’s offline error web pageThe request to the homepage failed so we clearly see this in consequence. (Giant preview)

That’s because the initial request to / (which can load the index.html file) will fail as a result of the Web is offline. The only approach for us to get well from that request failure is by having a service employee.

Let’s visualize the request with no service employee:

Animation of a network request going from the client’s browser to the internet.Community request goes from the browser to the Web and again. (Icons from (Giant preview)

A service worker is a programmable network proxy, which suggests it sits in between your net page and the Internet. This allows you to control incoming and outgoing network requests.

Animation of a network request intercepted by the service worker.Community request gets intercepted by the service employee. (Icons from (Giant preview)

This is useful as a result of we will now re-route this failed request to the cache (assuming we now have the content material within the cache).

Animation of a network request interecepted by the service worker and redirected to the cache.Network request will get redirected to the cache when it already exists within the cache. (Icons from (Giant preview)

A service employee can also be a kind of Net Employee, which means that it runs individually out of your fundamental web page and doesn’t have access to either the window or doc object.

Precache The App Shell

With a view to make our app work offline, we’re going to start out by precaching the app shell.

So let’s start by putting in the Webpack Workbox plugin:

npm set up –save-dev workbox-webpack-plugin

Then we’re going to open our index.js file and register the service worker:

if (“serviceWorker” in navigator)
window.addEventListener(“load”, () =>

Subsequent, open the webpack.config.js file and let’s configure the Workbox webpack plugin:

//add at the prime
const WorkboxWebpackPlugin = require(“workbox-webpack-plugin”);

//add contained in the plugins array:
plugins: [

, new WorkboxWebpackPlugin.InjectManifest(
swSrc: “./src/src-sw.js”,
swDest: “sw.js”

It will instruct Workbox to use our ./src/src-sw.js file as a base. The generated file shall be referred to as sw.js and will probably be within the dist folder.

Then create a ./src/src-sw.js file on the root degree and write the next inside it:


Observe: The self.__precacheManifest variable will probably be imported from a file that can be dynamically generated by workbox.

Now you’re able to build your code with npm run construct and Workbox will generate two information contained in the dist folder:

  • precache-manifest.66cf63077c7e4a70ba741ee9e6a8da29.js
  • sw.js

The sw.js imports workbox from CDN as well as the precache-manifest.[chunkhash].js.

//precache-manifest.[chunkhash].js file
self.__precacheManifest = (self.__precacheManifest || []).concat([

“revision”: “ba8f7488757693a5a5b1e712ac29cc28”,
“url”: “index.html”

“url”: “main.49467c51ac5e0cb2b58e.js”


The precache manifest lists the names of the information that have been processed by webpack and that find yourself in your dist folder. We’ll use these information to precache them within the browser. Because of this when your web site masses the primary time and registers the service worker, it’s going to cache these belongings in order that they can be used the subsequent time.

You may also discover that some entries have a ‘revision’ whereas others don’t. That’s as a result of the revision can typically be inferred from the chunkhash from the file identify. For instance, let’s take a better take a look at the file identify major.49467c51ac5e0cb2b58e.js. It has a revision is within the filename, which is the chunkhash 49467c51ac5e0cb2b58e.

This enables Workbox to know when your information change in order that it solely cleans up or updates the information that have been modified, somewhat than dumping all of the cache every time you publish a brand new version of your service employee.

The first time you load the web page, the service worker will install. You’ll be able to see that in DevTools. First, the sw.js file is requested which then requests all the opposite information. They are clearly marked with the gear icon.

Screenshot of DevTools Network tab when the service worker will get put in.Requests marked with the ⚙️ icon are requests initiated by the service employee. (Giant preview)

So Workbox will initialize and it will precache all of the information which are in the precache-manifest. It is very important double verify that you simply don’t have any unnecessary information in the precache-manifest file similar to .map information or information that are not a part of the app shell.

Within the network tab, we will see the requests coming from the service employee. And now for those who attempt to go offline, the app shell is already precached so it really works even when we’re offline!

Screenshot of Dev Tools Network tab displaying API calls failing.API calls fail once we go offline. (Giant preview)

Cache Dynamic Routes

Did you notice that once we went offline, the app shell works but not our knowledge? That’s as a result of these API calls aren’t part of the precached app shell. When there’s no Web connection, these requests will fail and the consumer gained’t be capable of see the foreign money info.

Nevertheless, these requests cannot be precached as a result of their worth comes from an API. Moreover, whenever you begin having a number of pages, you don’t need to cache all API request in a single go. As an alternative, you need to cache them when the consumer visits that web page.

We call these ‘dynamic data’. They typically embrace API calls in addition to pictures and other belongings which might be requested when a consumer does a sure action in your web site (e.g. once they browse to a brand new page).

You possibly can cache these using Workbox’s routing module. Here’s how:

//add in src/src-sw.js
new workbox.strategies.NetworkFirst(
cacheName: “currencies”,
new workbox.expiration.Plugin(
maxAgeSeconds: 10 * 60 // 10 minutes


It will arrange dynamic caching for any request URL that matches the URL

The caching technique that we used here is known as NetworkFirst; there are two different ones which are typically used:

  1. CacheFirst
  2. StaleWhileRevalidate

CacheFirst will search for it in the cache first. If it’s not discovered, then it’s going to get it from the community. StaleWhileRevalidate will go to the network and the cache at the similar time. Return the cache’s response to the page (while within the background) it is going to use the new community response to replace the cache for the subsequent time it’s used.

For our use case, we needed to go together with NetworkFirst as a result of we’re dealing with foreign money rates that change fairly often. Nevertheless, when the consumer goes offline, we will a minimum of present them the charges as they have been 10 minutes ago — that’s why we used the expiration plugin with the maxAgeSeconds set to 10 * 60 seconds.

Handle App Updates

Everytime a consumer masses your page, the browser will run the navigator.serviceWorker.register code although the service worker is already put in and operating. This enables the browser to detect if there’s a brand new model of the service employee. When the browser notices that the file has not modified, it just skips the registration call. Once that file modifications, the browser understands that there’s a new version of the service employee, thus it installs the new service employee parallel to the presently operating service employee.

Nevertheless, it pauses on the put in/ready part because only one service employee might be activated at the similar time.

The life cycle of a service worker: Parsed, Put in/Waiting, Activated & RedundantA simplified life cycle of a service employee (Giant preview)

Only when all the browser windows managed by the previous service employee are installed, then it turns into protected for the brand new service employee to activate.

It’s also possible to manually control that by calling skipWaiting() (or self.skipWaiting() since self is the global execution context within the service worker). Nevertheless, most of the time you must solely do this after asking the consumer if they need to get the newest replace.

Fortunately, workbox-window helps us achieve this. It’s a brand new window library introduced in Workbox v4 that aims at simplifying widespread duties on the window’s aspect.

Let’s begin by installing it with the following:

npm install workbox-window

Subsequent, import Workbox at the prime of the file index.js:

import Workbox from “workbox-window”;

Then we’ll substitute our registration code with the under:

if (“serviceWorker” in navigator)
window.addEventListener(“load”, () =>
const wb = new Workbox(“/sw.js”);


We’ll then find the replace button which has the ID app-update and pay attention for the workbox-waiting occasion:

//add before the wb.register()
const updateButton = doc.querySelector(“#app-update”);
// Fires when the registered service employee has put in but is waiting to activate.
wb.addEventListener(“waiting”, event =>
updateButton.addEventListener(“click”, () =>
// Set up a listener that may reload the page as quickly because the previously waiting service worker has taken control.
wb.addEventListener(“controlling”, event =>

// Send a message telling the service worker to skip waiting.
// It will set off the `controlling` event handler above.
wb.messageSW( sort: “SKIP_WAITING” );

This code will present the update button when there’s a new update (so when the service employee is in a waiting state) and will ship a SKIP_WAITING message to the service employee.

We’ll need replace the service worker file and handle the SKIP_WAITING occasion such that it calls the skipWaiting:

//add in src-sw.js
addEventListener(“message”, occasion =>
if (occasion.knowledge && event.knowledge.sort === “SKIP_WAITING”)


Now run npm run dev then reload the page. Go into your code and replace the navbar title to “Navbar v2”. Reload the web page once more, and you need to have the ability to see the replace icon.

Wrapping Up

Our web site now works offline and is ready to inform the consumer about new updates. Please take note though, that crucial issue when building a PWA is the consumer expertise. All the time concentrate on building experiences which might be straightforward to make use of by your users. We, as developers, are likely to get too enthusiastic about know-how and sometimes end up forgetting about our users.

When you’d wish to take this a step further, you possibly can add an internet app manifest which can permit your users so as to add the location to their house display. And in case you’d wish to know more about Workbox, you will discover the official documentation on the Workbox web site.

Additional Reading on SmashingMag:

Smashing Editorial(dm, yk, il)