imuigai-next-pwa
v0.0.8
Published
Next.js with PWA, powered by workbox.
Downloads
17
Maintainers
Readme
Zero Config PWA Plugin for Next.js
pwa library for creating webapps in Next.js . Powered by workbox.
Features
- 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
- Use workbox and workbox-window v7
- Work with cookies out of the box
- Default range requests for audios and videos
- No custom server needed for Next.js 13+
- Handle PWA lifecycle events opt-in
- Custom worker to run extra code with code splitting and typescript support
- Public environment variables available in custom worker as usual
- Debug service worker with confidence in development mode without caching
- 🛠 Configurable by the same workbox configuration options for GenerateSW and InjectManifest
NOTE 1 -
imuigai-next-pwa
version 0.0.1+ should only work withnext.js
13.0.0+, and static files should only be served throughpublic
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 innext.config.js
.
Install
If you are new to
next.js
orreact.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.
yarn add imuigai-next-pwa
or
npm i imuigai-next-pwa
Basic Usage
Step 1: withPWA
Update or create next.config.js
with
const withPWA = require('imuigai-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
imuigai-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)
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="@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/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'
/>
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
imuigai-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('imuigai-next-pwa')({
dest: 'public'
// disable: process.env.NODE_ENV === 'development',
// register: true,
// scope: '/app',
// sw: 'service-worker.js',
//...
})
module.exports = withPWA({
// next.js config
})
Other Options
imuigai-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
imuigai-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!
Tips
- Common UX pattern to ask user to reload when new service worker is installed
- Use a convention like
{command: 'doSomething', message: ''}
object whenpostMessage
to service worker. So that on the listener, it could do multiple different tasks usingif...else...
. - When you are debugging service worker, constantly
clean application cache
to reduce some flaky errors. - 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 asoptions.cacheableResponse.statuses=[200,302]
. - When debugging issues, you may want to format your generated
sw.js
file to figure out what's really going on. - Force
imuigai-next-pwa
to generate worker box production build by specify the optionmode: 'production'
in yourpwa
section ofnext.config.js
. Thoughimuigai-next-pwa
automatically generate the worker box development build during development (by runningnext
) and worker box production build during production (by runningnext build
andnext start
). You may still want to force it to production build even during development of your web app for following reason:- Reduce logging noise due to production build doesn't include logging.
- Improve performance a bit due to production build is optimized and minified.
- If you just want to disable worker box logging while keeping development build during development, simply put
self.__WB_DISABLE_DEV_LOGS = true
in yourworker/index.js
(create one if you don't have one). - 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
- Google Workbox
- ServiceWorker, MessageChannel, & postMessage by Nicolás Bevacqua
- The Service Worker Lifecycle
- 6 Tips to make your iOS PWA feel like a native app
- Make Your PWA Available on Google Play Store
License
MIT