Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork338
Zero config PWA plugin for Next.js, with workbox 🧰
License
shadowwalker/next-pwa
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
This plugin is powered byworkbox and other good stuff.
👋 Share your awesome PWA project 👉here
Features
- 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 fallbacksexample 🆕
- 📦 Useworkbox andworkbox-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-inexample
- 📐 Custom worker to run extra code with code splitting andtypescript supportexample
- 📜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-i18nextexample - 🛠 Configurable by the sameworkbox configuration options forGenerateSW andInjectManifest
- 🚀 Spin up aGitPod and try out examples in rocket speed
- ⚡ Supportblitz.js (simply add to
blitz.config.js) - 🔩 (Experimental) precaching
.module.jswhennext.config.jshasexperimental.modernset totrue
NOTE 1 -
next-pwaversion 2.0.0+ should only work withnext.js9.1+, and static files should only be served throughpublicdirectory. 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.
If you are new to
next.jsorreact.jsat all, you may want to first checkoutlearn next.js ornext.js document. Then start froma simple example orprogressive-web-app example in next.js repository.
yarn add next-pwa
Update or createnext.config.js with
constwithPWA=require('next-pwa')({dest:'public'})module.exports=withPWA({// next.js config})
After runningnext build, this will generate two files in yourpublic:workbox-*.js andsw.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.
Copy files to your static file hosting server, so that they are accessible from the following paths:https://yourdomain.com/sw.js andhttps://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.
When an HTTP request is received, test if those files are requested, then return those static files.
Exampleserver.js
const{ createServer}=require('http')const{ join}=require('path')const{ parse}=require('url')constnext=require('next')constapp=next({dev:process.env.NODE_ENV!=='production'})consthandle=app.getRequestHandler()app.prepare().then(()=>{createServer((req,res)=>{constparsedUrl=parse(req.url,true)const{ pathname}=parsedUrlif(pathname==='/sw.js'||/^\/(workbox|worker|fallback)-\w+\.js$/.test(pathname)){constfilePath=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-pwaplugin, and you probably have already set them up. If not, go ahead and set them up.
Create amanifest.json file in yourpublic 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"}Add the following into_document.jsx or_app.tsx, in<Head>:
<metaname="application-name"content="PWA App"/><metaname="apple-mobile-web-app-capable"content="yes"/><metaname="apple-mobile-web-app-status-bar-style"content="default"/><metaname="apple-mobile-web-app-title"content="PWA App"/><metaname="description"content="Best PWA App in the world"/><metaname="format-detection"content="telephone=no"/><metaname="mobile-web-app-capable"content="yes"/><metaname="msapplication-config"content="/icons/browserconfig.xml"/><metaname="msapplication-TileColor"content="#2B5797"/><metaname="msapplication-tap-highlight"content="no"/><metaname="theme-color"content="#000000"/><linkrel="apple-touch-icon"href="/icons/touch-icon-iphone.png"/><linkrel="apple-touch-icon"sizes="152x152"href="/icons/touch-icon-ipad.png"/><linkrel="apple-touch-icon"sizes="180x180"href="/icons/touch-icon-iphone-retina.png"/><linkrel="apple-touch-icon"sizes="167x167"href="/icons/touch-icon-ipad-retina.png"/><linkrel="icon"type="image/png"sizes="32x32"href="/icons/favicon-32x32.png"/><linkrel="icon"type="image/png"sizes="16x16"href="/icons/favicon-16x16.png"/><linkrel="manifest"href="/manifest.json"/><linkrel="mask-icon"href="/icons/safari-pinned-tab.svg"color="#5bbad5"/><linkrel="shortcut icon"href="/favicon.ico"/><linkrel="stylesheet"href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"/><metaname="twitter:card"content="summary"/><metaname="twitter:url"content="https://yourdomain.com"/><metaname="twitter:title"content="PWA App"/><metaname="twitter:description"content="Best PWA App in the world"/><metaname="twitter:image"content="https://yourdomain.com/icons/android-chrome-192x192.png"/><metaname="twitter:creator"content="@DavidWShadow"/><metaproperty="og:type"content="website"/><metaproperty="og:title"content="PWA App"/><metaproperty="og:description"content="Best PWA App in the world"/><metaproperty="og:site_name"content="PWA App"/><metaproperty="og:url"content="https://yourdomain.com"/><metaproperty="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
viewporthead meta tag into_app.jsrather than in_document.jsif you need it.
<metaname='viewport'content='minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no, user-scalable=no, viewport-fit=cover'/>
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 aspages/_offline.js orpages/_offline.jsx orpages/_offline.ts orpages/_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 tohandlerDidError plugin to allruntimeCaching configs, so that precached resources are served when fetch failed.
You can also setupprecacheFallback.fallbackURL in yourruntimeCaching 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.
There are options you can use to customize the behavior of this plugin by addingpwa object in the next config innext.config.js:
constwithPWA=require('next-pwa')({dest:'public'// disable: process.env.NODE_ENV === 'development',// register: true,// scope: '/app',// sw: 'service-worker.js',//...})module.exports=withPWA({// next.js config})
- disable: boolean - whether to disable pwa feature as a whole
- default:
false - set
disable: false, so that it will generate service worker in bothdevandprod - set
disable: trueto completely disable PWA - if you don't need to debug service worker in
dev, you can setdisable: process.env.NODE_ENV === 'development'
- default:
- register: boolean - whether to let this plugin register service worker for you
- default to
true - set to
falsewhen you want to handle register service worker yourself, this could be done incomponentDidMountof your root app. you can consider theregister.js as an example.
- default to
- scope: string - url scope for pwa
- default:
basePathinnext.config.jsor/ - set to
/appso that path under/appwill be PWA while others are not
- default:
- sw: string - service worker script file name
- default:
/sw.js - set to another file name if you want to customize the output file name
- default:
- runtimeCaching - caching strategies (array or callback function)
- default: see theRuntime 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, pleaseALWAYS 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
publicfolder from being precached.- default:
['!noprecache/**/*']- this means that the default behavior will precache all the files inside yourpublicfolder but files inside/public/noprecachefolder. 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']
- default:
- 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 withnext-optimized-imagesplugin) - 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.
- default:
- 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
cacheStartUrlset totrue - recommend: set tofalse if your start url always returns same HTML document, then start url will be precached, this will help to speed up first load.
- default:
- 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
dynamicStartUrlRedirectset totrue
- default:
- 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
/_offlinepage such aspages/_offline.jsand you are all set, no configuration necessary - default:
objectfallbacks.document- fallback route for document (page), default to/_offlineif you created that pagefallbacks.image- fallback route for image, default to nonefallbacks.audio- fallback route for audio, default to nonefallbacks.video- fallback route for video, default to nonefallbacks.font- fallback route for font, default to none
- if you just need a offline fallback page, simply create a
- cacheOnFrontEndNav - enable additional route cache when navigate between pages with
next/linkon front end. Checkout thisexample 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.
- default:
subdomainPrefix: string - url prefix to allow hosting static files on a subdomaindefault:""- i.e. default with no prefixexample:/subdomainif the app is hosted onexample.com/subdomain- deprecated, usebasePath 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
- default:
- customWorkerDir - customize the directory where
next-pwalooks for a custom worker implementation to add to the service worker generated by workbox. For more information, check out thecustom worker example.- default:
worker
- default:
next-pwa usesworkbox-webpack-plugin, other options which could also be put inpwa object can be foundON THE DOCUMENTATION forGenerateSW andInjectManifest. If you specifyswSrc,InjectManifest plugin will be used, otherwiseGenerateSW will be used to generate service worker.
next-pwa uses a default runtimecache.js
There is a great chance you may want to customize your own runtime caching rules. Please feel free to copy the defaultcache.js file and customize the rules as you like. Don't forget to inject the configurations into yourpwa config innext.config.js.
Here is thedocument on how to write runtime caching configurations, including background sync and broadcast update features and more!
- Common UX pattern to ask user to reload when new service worker is installed
- Use a convention like
{command: 'doSomething', message: ''}object whenpostMessageto service worker. So that on the listener, it could do multiple different tasks usingif...else.... - When you are debugging service worker, constantly
clean application cacheto reduce some flaky errors. - If you are redirecting the user to another route, please noteworkbox 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
runtimeCachingsuch asoptions.cacheableResponse.statuses=[200,302]. - When debugging issues, you may want to format your generated
sw.jsfile to figure out what's really going on. - Force
next-pwato generate worker box production build by specify the optionmode: 'production'in yourpwasection ofnext.config.js. Thoughnext-pwaautomatically generate the worker box development build during development (by runningnext) and worker box production build during production (by runningnext buildandnext 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 = truein yourworker/index.js(create one if you don't have one). - It is common developers have to use
userAgentstring to determine if users are using Safari/iOS/MacOS or some other platform,ua-parser-js library is a good friend for that purpose.
- Google Workbox
- ServiceWorker, MessageChannel, & postMessage byNicolá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
- Experience SAMSUNG on an iPhone - must open on an iPhone to start
- App Scope - like an app store for PWA
- PWA Directory
- PWA Builder - Alternative way to build awesome PWA
MIT
About
Zero config PWA plugin for Next.js, with workbox 🧰
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.