> For the complete documentation index, see [llms.txt](https://fe-qasir.gitbook.io/qiblat-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://fe-qasir.gitbook.io/qiblat-documentation/integrations/pwa.md).

# PWA

## Features

* Zero config for registering and generating a service worker
* Optimized precache and runtime cache
* Maximize lighthouse score
* Easy to understand examples
* Completely offline support
* Use workbox and workbox-window v6
* Work with cookies out of the box
* No custom server needed for Next.js 9+
* Handle PWA lifecycle events opt-in
* Custom worker to run extra code in service worker with code splitting
* Debug service worker with confidence in development mode without caching
* Internationalization (a.k.a I18N) with `next-i18next`
* Configurable by the same [workbox configuration options](https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin) for [GenerateSW](https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-webpack-plugin.GenerateSW) and [InjectManifest](https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-webpack-plugin.InjectManifest)
* Spin up a [GitPod](https://gitpod.io/#https://github.com/shadowwalker/next-pwa/) and try out examples in rocket speed
* (Experimental) precaching `.module.js` when `next.config.js` has `experimental.modern` set to `true`

## How to use

Thanks to **Next.js 9+**, we can use the `public` folder to serve static files from the root `/` URL path. It cuts the need to write custom server only to serve those files. Therefore the setup is easier and concise. We can use `next.config.js` to config `next-pwa` to generates service worker and workbox files into the `public` folder.

### Step 1: Install library

```bash
yarn add next-pwa
```

### Step 2: withPWA

Update or create `next.config.js` with

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

module.exports = withPWA({
  pwa: {
    disable: process.env.NODE_ENV === 'development',
    register: true,
    scope: '/app',
    sw: 'service-worker.js',
    dest: 'public'
  }
})
```

#### Available Options

* disable: boolean - whether to disable pwa feature as a whole
  * default to `false`
  * set `disable: false`, so that it will generate service worker in both `dev` and `prod`
  * set `disable: true` to completely disable PWA
  * if you don't need to debug service worker in `dev`, you can set `disable: process.env.NODE_ENV === 'development'`
* register: boolean - whether to let this plugin register service worker for you
  * default to `true`
  * set to `false` when you want to handle register service worker yourself, this could be done in `componentDidMount` of your root app. you can consider the [register.js](https://github.com/shadowwalker/next-pwa/blob/master/register.js) as an example.
* scope: string - url scope for pwa
  * default to `/`
  * set to `/app` so that path under `/app` will be PWA while others are not
* sw: string - service worker script file name
  * default to `/sw.js`
  * set to another file name if you want to customize the output file name
* runtimeCaching - caching strategies (array or callback function)
  * default: see the **Runtime Caching** section for the default configuration
  * accepts an array of cache entry objects, [please follow the structure here](https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.RuntimeCachingEntry)
  * Note: the order of the array matters. The first rule that matches is effective. Therefore, please **ALWAYS** put rules with larger scope behind the rules with a smaller and specific scope.
* publicExcludes - an array of glob pattern strings to exclude files in the `public` folder from being precached.
  * default: `[]` - this means that the default behavior will precache all the files inside your `public` folder
  * example: `['!img/super-large-image.jpg', '!fonts/not-used-fonts.otf']`
* buildExcludes - an array of extra pattern or function to exclude files from being precached in `.next/static` (or your custom build) folder
  * default: `[]`
  * example: `[/chunks\/images\/.*$/]` - Don't precache files under `.next/static/chunks/images` (Highly recommend this to work with `next-optimized-images` plugin)
  * doc: Array of (string, RegExp, or function()). One or more specifiers used to exclude assets from the precache manifest. This is interpreted following the same rules as Webpack's standard exclude option.
* subdomainPrefix: string - url prefix to allow hosting static files on a subdomain
  * default: `""` - i.e. default with no prefix
  * example: `/subdomain` if the app is hosted on `example.com/subdomain`

### Step 3: Add Manifest File&#x20;

Create a `manifest.json` file in your `public` folder:

```jsx
{
  "name": "Qiblat",
  "short_name": "Qiblat",
  "theme_color": "#F04B32",
  "background_color": "#F04B32",
  "display": "fullscreen",
  "orientation": "portrait",
  "scope": "/",
  "start_url": "/",
  "icons": [
    {
      "src": "img/logo/manifest/logo-72x72.png",
      "sizes": "72x72",
      "type": "image/png"
    },
    {
      "src": "img/logo/manifest/logo-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
    },
    {
      "src": "img/logo/manifest/logo-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "img/logo/manifest/logo-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "img/logo/manifest/logo-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "img/logo/manifest/logo-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "img/logo/manifest/logo-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "img/logo/manifest/logo-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "splash_pages": null
}
```

### Step 4: Add Head Meta

Add the following into `_document.jsx` or `_document.tsx`, in `<Head>`:

```jsx
<meta name='application-name' content='PWA App' />
<meta name='apple-mobile-web-app-capable' content='yes' />
<meta name='apple-mobile-web-app-status-bar-style' content='default' />
<meta name='apple-mobile-web-app-title' content='PWA App' />
<meta name='description' content='Best PWA App in the world' />
<meta name='format-detection' content='telephone=no' />
<meta name='mobile-web-app-capable' content='yes' />
<meta name='msapplication-config' content='/static/icons/browserconfig.xml' />
<meta name='msapplication-TileColor' content='#2B5797' />
<meta name='msapplication-tap-highlight' content='no' />
<meta name='theme-color' content='#000000' />
          
<link rel='apple-touch-icon' sizes='180x180' href='/static/icons/apple-touch-icon.png' />
<link rel='icon' type='image/png' sizes='32x32' href='/static/icons/favicon-32x32.png' />
<link rel='icon' type='image/png' sizes='16x16' href='/static/icons/favicon-16x16.png' />
<link rel='manifest' href='/static/manifest.json' />
<link rel='mask-icon' href='/static/icons/safari-pinned-tab.svg' color='#5bbad5' />
<link rel='shortcut icon' href='/static/icons/favicon.ico' />
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:300,400,500' />
     
<meta name='twitter:card' content='summary' />
<meta name='twitter:url' content='https://yourdomain.com' />
<meta name='twitter:title' content='PWA App' />
<meta name='twitter:description' content='Best PWA App in the world' />
<meta name='twitter:image' content='https://yourdomain.com/static/icons/android-chrome-192x192.png' />
<meta name='twitter:creator' content='@DavidWShadow' />
<meta property='og:type' content='website' />
<meta property='og:title' content='PWA App' />
<meta property='og:description' content='Best PWA App in the world' />
<meta property='og:site_name' content='PWA App' />
<meta property='og:url' content='https://yourdomain.com' />
<meta property='og:image' content='https://yourdomain.com/static/icons/apple-touch-icon.png' />
```

{% hint style="info" %}
Tip: Put the `viewport` head meta tag into `_app.js` rather than in `_document.js` if you need it.
{% endhint %}

```jsx
<meta name='viewport' content='minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no, user-scalable=no, viewport-fit=cover' />
```
