Progressive Web App (PWA) in Next.js
Tags: nextjs, react
Published: 2021-10-23 16:23:08

---
title: Progressive Web App (PWA) in Next.js
published: true
date: 2021-10-23 00:00:00 UTC
tags: nextjs, react
canonical_url: https://bionicjulia.com/blog/progressive-web-app-pwa-in-nextjs
description: Creating a PWA using next-pwa with offline support

---

### First things first - what even is a Progressive Web App, or PWA for short?

If you've got some time, check out the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Introduction) to get a good overview. If I had to take a stab at defining PWAs in my own words, it's a way of allowing a web app to be accessed as if it was a native app, on mobile or desktop devices.

### So, what is it that actually makes an app, a Progressive Web App?

Whilst I don't think there's an official standard on this, some of the key principles to consider are:

- Is it installable, so that it can be accessed, say on your mobile home screen?
- Does it work offline / under poor network connections?
- Is it responsive to whatever device it's being viewed on?
- Does it adhere to progressive enhancement, in that it caters to older devices (at a more basic level), but also newer ones?

### Why might you want to consider making your web app into a PWA?

Whilst it can be simple enough to allow your users to continue accessing your app through your mobile browser, allowing it to be accessed as a pseudo native app directly from a home screen may:

- Be more efficient;
  - Loading times can be near instantaneous due to the use of service workers and caching.
  - You only need to send updates for bits that have actually changed, as opposed to updating an entire native app.
- Provide a nicer native experience vs. a web browser e.g. with mobile-specific app icons, responsive design and features and full screen modes);
- Allow your users to access your app without a network connection; and
- Provide more direct connection and engagement with your end user through use of features like push notifications.

### How to create a PWA in Next.js

The really nice thing about Next.js is that they have an official way of doing this with the [next-pwa](https://github.com/shadowwalker/next-pwa) package. Here are the steps I took:

- Install the package with `yarn add next-pwa`.
- If you don't already have one, create a `manifest.json` file in your `/public` folder. This file basically provides metadata about your app, to your browser, so that it knows how to render your app when downloaded as an extension on desktop or mobile. You can search for manifest generators online, but the one thing you'll need beforehand is an icon for your app. You can see an example of what my manifest file looks like in the appendix below.
- We now need make the `manifest.json` file accessible on browser load. Within the `/pages` directory, open (or create) the `_document.tsx` file. If you've not come across this file before, read more about it [here](https://nextjs.org/docs/advanced-features/custom-document). You basically want to add a link to your manifest JSON within the `<Head>` tags so that your browser can access it. Alternatively, if you've already got access to your app's `<Head>` tags somewhere else (e.g. `index.tsx`), then just put it in there.
  ```jsx
  <Head>
    // ...
    <link rel="manifest" href="/manifest.json" />
    // ...
  </Head>
  ```
- In `next.config.js`, require the `next-pwa` package and wrap your module export function with it. (Detailed instructions are available in the `next-pwa` Github repo.)

  ```jsx
  const withPWA = require('next-pwa')

  module.exports = withPWA({
    target: 'serverless',
    pwa: {
      dest: 'public',
      disable: process.env.NODE_ENV === 'development',
    },
    // ... whatever other config you might have
  })
  ```

  Note that there are other settings you can configure your PWA to adhere to, so just check out the official documentation to learn more.

And that's basically the setup in a nutshell. To test it's all working as expected, you can uncomment out the `disable` line for the dev environment in the config file above, then build your app in Next (I use `yarn build` for this). Head over to `localhost:3000` and you should see an option in your browser to "install" your app.

This is what it looks like on my production site:
![How to install the PWA](https://user-images.githubusercontent.com/7389546/138563610-971ce70e-659b-415f-841e-da9e8c9907c4.png)

Note that running this build for the first time will create a number of new PWA files in your `public` folder, including `sw.js` which is your service worker. You're going to want to ensure these don't get cached so remember to ignore these files in your version control (see troubleshooting tips below).

### Offline support

One thing to note is that the `next-pwa` package tries to load content by grabbing it from the cache and network. If both of these sources fail, an error page will be rendered instead. For a nicer user experience, you can define a custom page for the user to see by creating a new file called `_offline.tsx` in the `pages` directory. All pages which cannot be downloaded will then display this page instead.

To overwrite the file location for this fallback page, in addition to setting up fallback content for other media types like images, videos, fonts etc., you can define these in the `next.config.js` file within the `pwa` settings.

```jsx
module.exports = withPWA({
  target: 'serverless',
  pwa: {
    dest: 'public',
    disable: process.env.NODE_ENV === 'development',
    fallbacks: {
      image: '/offline.png',
      document: '/pages/offline-2.tsx',
    },
  },
  // ...
})
```

This is what my offline page looks like:
![PWA offline page](https://user-images.githubusercontent.com/7389546/138563622-56a70654-be7b-4829-88f7-1e7c2c6a716a.png)

## Troubleshooting tips

- If you want to test it in the local dev environment, and see an error that looks something like this, you might need to install webpack as a dev dependency.
  ```jsx
  Could not find files for / in .next/build-manifest.json
  ```
  To do that, run `yarn add webpack --dev`.
- Ensure you keep content updated by adding the following PWA files to `.gitignore` (if you use git), so that these are not checked in and cached, but generated each time you build your app.

  ```bash
  # .gitignore

  /public/sw.js
  /public/workbox-*.js
  /public/worker-*.js
  /public/sw.js.map
  /public/workbox-*.js.map
  /public/worker-*.js.map
  /public/fallback-*.js
  ```

## Appendix

My `manifest.json` file:

```jsx
{
  "name": "Bionic Julia",
  "short_name": "Bionic Julia",
  "description": "My thoughts and learnings",
  "start_url": "/",
  "orientation": "portrait-primary",
  "icons": [
    {
      "src": "/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "theme_color": "#ffffff",
  "background_color": "#ffffff",
  "display": "standalone"
}
```

Liked this post? Let's continue the conversation on https://bionicjulia.com, [Twitter](https://twitter.com/bionicjulia) or [Instagram](https://instagram.com/bionicjulia).