• This repository has been archived on 18/Aug/2023
  • Stars
    star
    147
  • Rank 251,347 (Top 5 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 1 year ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Zero config PWA plugin for Next.js, with workbox 🧰 with appDir support

Zero Config PWA Plugin for Next.js with appDir support

Archived, please use https://github.com/ducanhgh/next-pwa/, there are already active fork of next-pwa out there

This plugin is powered by workbox and other good stuff.

size dependencies downloads license

Please ⭐️ star this repo if you find it useful πŸ‘†

Features

  • πŸš€ Next.js appDir support
  • 0️⃣ Zero config for registering and generating service worker
  • ✨ Optimized precache and runtime cache
  • πŸ’― Maximize lighthouse score
  • 🎈 Easy to understand examples
  • πŸ“΄ Completely offline support with fallbacks example πŸ†•
  • πŸ“¦ Use workbox and workbox-window v6
  • πŸͺ Work with cookies out of the box
  • πŸ”‰ Default range requests for audios and videos
  • β˜• No custom server needed for Next.js 9+ example
  • πŸ”§ Handle PWA lifecycle events opt-in example
  • πŸ“ Custom worker to run extra code with code splitting and typescript support example
  • πŸ“œ Public environment variables available in custom worker as usual
  • 🐞 Debug service worker with confidence in development mode without caching
  • 🌏 Internationalization (a.k.a I18N) with next-i18next example
  • πŸ›  Configurable by the same workbox configuration options for GenerateSW and InjectManifest
  • πŸš€ Spin up a GitPod and try out examples in rocket speed
  • ⚑ Support blitz.js (simply add to blitz.config.js)
  • πŸ”© (Experimental) precaching .module.js when next.config.js has experimental.modern set to true

NOTE 1 - @imbios/next-pwa should only work with next.js 9.1+, and static files should only be served through public directory. This will make things simpler.

NOTE 2 - If you encounter error TypeError: Cannot read property **'javascript' of undefined** during build, please consider upgrade to webpack5 in next.config.js.


Open in Gitpod

Install

If you are new to next.js or react.js at all, you may want to first checkout learn next.js or next.js document. Then start from a simple example or progressive-web-app example in next.js repository.

pnpm add @imbios/next-pwa
# or
yarn add @imbios/next-pwa
# or
npm install @imbios/next-pwa

Basic Usage

Step 1: withPWA

Update or create next.config.js with

const withPWA = require("@imbios/next-pwa")({
  dest: "public",
});

module.exports = withPWA({
  // next.js config
});

After running next build, this will generate two files in your public: workbox-*.js and sw.js, which will automatically be served statically.

If you are using Next.js version 9 or newer, then skip the options below and move on to Step 2.

If you are using Next.js older than version 9, you'll need to pick an option below before continuing to Step 2.

Option 1: Host Static Files

Copy files to your static file hosting server, so that they are accessible from the following paths: https://yourdomain.com/sw.js and https://yourdomain.com/workbox-*.js.

One example is using Firebase hosting service to host those files statically. You can automate the copy step using scripts in your deployment workflow.

For security reasons, you must host these files directly from your domain. If the content is delivered using a redirect, the browser will refuse to run the service worker.

Option 2: Use Custom Server

When an HTTP request is received, test if those files are requested, then return those static files.

Example server.js

const { createServer } = require("http");
const { join } = require("path");
const { parse } = require("url");
const next = require("next");

const app = next({ dev: process.env.NODE_ENV !== "production" });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  createServer((req, res) => {
    const parsedUrl = parse(req.url, true);
    const { pathname } = parsedUrl;

    if (
      pathname === "/sw.js" ||
      /^\/(workbox|worker|fallback)-\w+\.js$/.test(pathname)
    ) {
      const filePath = join(__dirname, ".next", pathname);
      app.serveStatic(req, res, filePath);
    } else {
      handle(req, res, parsedUrl);
    }
  }).listen(3000, () => {
    console.log(`> Ready on http://localhost:${3000}`);
  });
});

The following setup has nothing to do with next-pwa plugin, and you probably have already set them up. If not, go ahead and set them up.

Step 2: Add Manifest File (Example)

page Directory

Create a manifest.json file in your public folder:

{
  "name": "PWA App",
  "short_name": "App",
  "icons": [
    {
      "src": "/icons/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/icons/android-chrome-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "theme_color": "#FFFFFF",
  "background_color": "#FFFFFF",
  "start_url": "/",
  "display": "standalone",
  "orientation": "portrait"
}

Step 3: Add Head Meta (Example)

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

<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="/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" href="/icons/touch-icon-iphone.png" />
<link
  rel="apple-touch-icon"
  sizes="152x152"
  href="/icons/touch-icon-ipad.png"
/>
<link
  rel="apple-touch-icon"
  sizes="180x180"
  href="/icons/touch-icon-iphone-retina.png"
/>
<link
  rel="apple-touch-icon"
  sizes="167x167"
  href="/icons/touch-icon-ipad-retina.png"
/>

<link
  rel="icon"
  type="image/png"
  sizes="32x32"
  href="/icons/favicon-32x32.png"
/>
<link
  rel="icon"
  type="image/png"
  sizes="16x16"
  href="/icons/favicon-16x16.png"
/>
<link rel="manifest" href="/manifest.json" />
<link rel="mask-icon" href="/icons/safari-pinned-tab.svg" color="#5bbad5" />
<link rel="shortcut icon" href="/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/icons/android-chrome-192x192.png"
/>
<meta name="twitter:creator" content="@imamdev_" />
<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/icons/apple-touch-icon.png"
/>

<!-- apple splash screen images -->
<!--
<link rel='apple-touch-startup-image' href='/images/apple_splash_2048.png' sizes='2048x2732' />
<link rel='apple-touch-startup-image' href='/images/apple_splash_1668.png' sizes='1668x2224' />
<link rel='apple-touch-startup-image' href='/images/apple_splash_1536.png' sizes='1536x2048' />
<link rel='apple-touch-startup-image' href='/images/apple_splash_1125.png' sizes='1125x2436' />
<link rel='apple-touch-startup-image' href='/images/apple_splash_1242.png' sizes='1242x2208' />
<link rel='apple-touch-startup-image' href='/images/apple_splash_750.png' sizes='750x1334' />
<link rel='apple-touch-startup-image' href='/images/apple_splash_640.png' sizes='640x1136' />
-->

Tip: Put the viewport head meta tag into _app.js rather than in _document.js if you need it.

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

app Directory

import { Metadata } from "next";

/** Add your relevant code here for the issue to reproduce */
export default function Home() {
  return <h1>PWA πŸ’– Next.js</h1>;
}

const APP_NAME = "next-pwa example";
const APP_DESCRIPTION = "This is an example of using next-pwa plugin";

export const metadata: Metadata = {
  title: "PWA πŸ’– Next.js",
  description: APP_DESCRIPTION,
  twitter: {
    card: "summary_large_image",
    creator: "@imamdev_",
    images: "https://example.com/og.png",
  },
  applicationName: APP_NAME,
  appleWebApp: {
    capable: true,
    title: APP_NAME,
    statusBarStyle: "default",
  },
  formatDetection: {
    telephone: false,
  },
  themeColor: "#FFFFFF",
  viewport:
    "minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no, viewport-fit=cover",
  manifest: "/manifest.json",
  icons: [
    { rel: "apple-touch-icon", url: "/icons/apple-touch-icon.png" },
    { rel: "shortcut icon", url: "/favicon.ico" },
  ],
  keywords: ["nextjs", "pwa", "next-pwa"],
};

Offline Fallbacks

Offline fallbacks are useful when the fetch failed from both cache and network, a precached resource is served instead of present an error from browser.

To get started simply add a /_offline page such as pages/_offline.js or pages/_offline.jsx or pages/_offline.ts or pages/_offline.tsx. Then you are all set! When the user is offline, all pages which are not cached will fallback to '/_offline'.

Use this example to see it in action

next-pwa helps you precache those resources on the first load, then inject a fallback handler to handlerDidError plugin to all runtimeCaching configs, so that precached resources are served when fetch failed.

You can also setup precacheFallback.fallbackURL in your runtimeCaching config entry to implement similar functionality. The difference is that above method is based on the resource type, this method is based matched url pattern. If this config is set in the runtimeCaching config entry, resource type based fallback will be disabled automatically for this particular url pattern to avoid conflict.

Configuration

There are options you can use to customize the behavior of this plugin by adding pwa object in the next config in next.config.js:

const withPWA = require("@imbios/next-pwa")({
  dest: "public",
  // disable: process.env.NODE_ENV === 'development',
  // register: true,
  // scope: '/app',
  // sw: 'service-worker.js',
  //...
});

module.exports = withPWA({
  // next.js config
});

Available Options

  • disable: boolean - whether to disable pwa feature as a whole
    • default: 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 as an example.
  • scope: string - url scope for pwa
    • default: basePath in next.config.js or /
    • set to /app so that path under /app will be PWA while others are not
  • sw: string - service worker script file name
    • default: /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
    • 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: ['!noprecache/**/*'] - this means that the default behavior will precache all the files inside your public folder but files inside /public/noprecache folder. You can simply put files inside that folder to not precache them without config this.
    • 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.
  • cacheStartUrl - whether to cache start url
  • dynamicStartUrl - if your start url returns different HTML document under different state (such as logged in vs. not logged in), this should be set to true.
    • default: true
    • effective when cacheStartUrl set to true
    • recommend: set to false if your start url always returns same HTML document, then start url will be precached, this will help to speed up first load.
  • dynamicStartUrlRedirect - if your start url redirect to another route such as /login, it's recommended to setup this redirected url for the best user experience.
    • default: undefined
    • effective when dynamicStartUrlRedirect set to true
  • fallbacks - config precached routes to fallback when both cache and network not available to serve resources.
    • if you just need a offline fallback page, simply create a /_offline page such as pages/_offline.js and you are all set, no configuration necessary
    • default: object
      • fallbacks.document - fallback route for document (page), default to /_offline if you created that page
      • fallbacks.image - fallback route for image, default to none
      • fallbacks.audio - fallback route for audio, default to none
      • fallbacks.video - fallback route for video, default to none
      • fallbacks.font - fallback route for font, default to none
  • cacheOnFrontEndNav - enable additional route cache when navigate between pages with next/link on front end. Checkout this example for some context about why this is implemented.
    • default: false
    • note: this improve user experience on special use cases but it also adds some overhead because additional network call, I suggest you consider this as a trade off.
  • 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
    • deprecated, use basePath instead
  • reloadOnOnline - changes the behaviour of the app when the device detects that it has gone back "online" and has a network connection. Indicate if the app should call location.reload() to refresh the app.
    • default: true
  • customWorkerDir - customize the directory where next-pwa looks for a custom worker implementation to add to the service worker generated by workbox. For more information, check out the custom worker example.
    • default: worker
  • customWorkerWebpack - Function to customize the webpack configuration for bundling custom workers. Receives the configuration object with default settings and must return the modified one.
    • default: undefined

Other Options

next-pwa uses workbox-webpack-plugin, other options which could also be put in pwa object can be found ON THE DOCUMENTATION for GenerateSW and InjectManifest. If you specify swSrc, InjectManifest plugin will be used, otherwise GenerateSW will be used to generate service worker.

Runtime Caching

next-pwa uses a default runtime cache.js

There is a great chance you may want to customize your own runtime caching rules. Please feel free to copy the default cache.js file and customize the rules as you like. Don't forget to inject the configurations into your pwa config in next.config.js.

Here is the document on how to write runtime caching configurations, including background sync and broadcast update features and more!

Recommend .gitignore

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

Tips

  1. Common UX pattern to ask user to reload when new service worker is installed
  2. Use a convention like {command: 'doSomething', message: ''} object when postMessage to service worker. So that on the listener, it could do multiple different tasks using if...else....
  3. When you are debugging service worker, constantly clean application cache to reduce some flaky errors.
  4. If you are redirecting the user to another route, please note workbox by default only cache response with 200 HTTP status, if you really want to cache redirected page for the route, you can specify it in runtimeCaching such as options.cacheableResponse.statuses=[200,302].
  5. When debugging issues, you may want to format your generated sw.js file to figure out what's really going on.
  6. Force next-pwa to generate worker box production build by specify the option mode: 'production' in your pwa section of next.config.js. Though next-pwa automatically generate the worker box development build during development (by running next) and worker box production build during production (by running next build and next start). You may still want to force it to production build even during development of your web app for following reason:
    1. Reduce logging noise due to production build doesn't include logging.
    2. Improve performance a bit due to production build is optimized and minified.
  7. If you just want to disable worker box logging while keeping development build during development, simply put self.__WB_DISABLE_DEV_LOGS = true in your worker/index.js (create one if you don't have one).
  8. It is common developers have to use userAgent string to determine if users are using Safari/iOS/MacOS or some other platform, ua-parser-js library is a good friend for that purpose.

Reference

  1. Google Workbox
  2. ServiceWorker, MessageChannel, & postMessage by NicolΓ‘s Bevacqua
  3. The Service Worker Lifecycle
  4. 6 Tips to make your iOS PWA feel like a native app
  5. Make Your PWA Available on Google Play Store

Fun PWA Projects

  1. Experience SAMSUNG on an iPhone - must open on an iPhone to start
  2. App Scope - like an app store for PWA
  3. PWA Directory
  4. PWA Builder - Alternative way to build awesome PWA

License

MIT

More Repositories

1

bun-node

Pre-configured Bun and Node.js Docker Images for Seamless, Optimized Development and Deployment.
Shell
109
star
2

qwiktober-2022

Simple website collection built with Qwik City for Hacktoberfest 2022
JavaScript
31
star
3

tailwind-color-filter-generator

Generate custom css and tailwind filter property to achieve any target color 🎨
JavaScript
17
star
4

remute

Use the Touch Bar app to Mute/Unmute your microphone, or just use the shortcut on any Mac.
Objective-C
16
star
5

cardus-app

This is the Trusted Web Activity for the CardusApp project. It is a webapp that allows you to create and manage your own personal warehouse/storage/carton.
TypeScript
11
star
6

gitmemoji

An app to learn gitmoji emojis
TypeScript
10
star
7

go-micho-twitter

Twitter-like API written in Go, using Echo framework.
Go
8
star
8

i18n-num-in-words

Number utility to convert number to words in any language.
TypeScript
8
star
9

RNWidget

Widgets using Expo and React Native for cross-platform mobile applications targeting Android and iOS.
Java
7
star
10

GAS-CertificateCreationAndEmail

Automated Certificate Generator & Email Sender with Google Apps Script
JavaScript
7
star
11

mute-me

Moved to ImBIOS/remute
Objective-C
4
star
12

lorust

Modern Rust utility library delivering modularity, performance & extras; or simply Rust version of Lodash
Rust
4
star
13

MyFlix-API

Netflix API clone built with Laravel
PHP
4
star
14

story-app

This is a story app that allows users to create stories.
JavaScript
2
star
15

next-to-ts

Codemod to help you migrate your Next.js project to typescript in seconds
JavaScript
2
star
16

kajiansegar

TypeScript
2
star
17

prisma-armv7-builds

This repository contains precompiled prisma-engines for armv7.
JavaScript
2
star
18

SharedPreferencesLoginAndLogout

Example project to learn login and logout mecahnism using shared preference in android studio with kotlin language.
Kotlin
2
star
19

ExamBrowser-Android

Kotlin
2
star
20

fantastic-robot

TypeScript
2
star
21

Exercism

My Exercism Journey
Zig
2
star
22

nextjs-netlify-blog-template

Next.js blogging template for Netlify is a boilerplate for building blogs with only Netlify stacks.
TypeScript
1
star
23

ImBIOS

README of ImBIOS
Python
1
star
24

bookshelf-apps

Using only HTML, CSS, and JavaScript; This is a bookshelf app that allows you to search for books and add them to your bookshelf. You can also remove books from your bookshelf.
JavaScript
1
star
25

nextjs-flask-lab

TypeScript
1
star
26

azurehackathon2022_frontend

TypeScript
1
star
27

calcom-docker

Shell
1
star
28

blogger-to-md

Convert Blogger & WordPress backup blog posts to markdown documents
JavaScript
1
star
29

idn-noob-website

A project to learn how the basic html, css and javascript work.
JavaScript
1
star
30

codecrafters-http-server-typescript

TypeScript
1
star
31

bun-to-rupiah-repro

TypeScript
1
star
32

java-springboot-demo

Java
1
star
33

nextjs-paypal

use Next.js with Typescript and Prisma to integrate payment with PayPal
TypeScript
1
star
34

nextjs-tauri-macos-titlebar

Template for Next.js + Tauri with macOS Title Bar Configured
TypeScript
1
star
35

demo-deploy-plasmic

JavaScript
1
star
36

html-tutorial-lab

HTML
1
star
37

template-porto-bootstrap

Pug
1
star
38

RetrofitExample

a CRUD operation lab with retrofit
Kotlin
1
star
39

Pi-Router-Smart-Controller

Controlling router power to auto-reboot if necessary.
Python
1
star
40

codelab-friendlyeats-web

Integrate Firebase with a Next.js app codelab
TypeScript
1
star
41

Botmind_Frontend_Technical_Assignment

This project is a web application that displays a map view of a school campus with a fleet of robots.
TypeScript
1
star
42

speechify-web-functional-test

TypeScript
1
star
43

silver-parakeet

PoC for Next.js with Editor.js integration
TypeScript
1
star
44

ts-ffi-interop-lab-0

TypeScript
1
star
45

ai-text-summarizer-app

"AI Text Summarizer App" is a tool designed for quick and efficient processing of large volumes of text.
JavaScript
1
star
46

next_microfrontend

This project demonstrates the implementation of a micro-frontend architecture using React and Next.js.
JavaScript
1
star
47

imbios-blog-rsc

MDX
1
star
48

codecrafters-bittorrent-zig

"Build Your Own BitTorrent" Challenge.
Zig
1
star
49

common-char-extractor-ts

Given an array of strings, write a function to find and return an array of characters that appear in the same position across all strings.
TypeScript
1
star
50

actor-bun-node-docker

Fork Base Docker images of Apify actors using Bun and Node
Dockerfile
1
star