Security News
ESLint is Now Language-Agnostic: Linting JSON, Markdown, and Beyond
ESLint has added JSON and Markdown linting support with new officially-supported plugins, expanding its versatility beyond JavaScript.
next-pwa is a plugin for Next.js that helps you turn your Next.js application into a Progressive Web App (PWA) with minimal configuration. It provides features like offline support, caching strategies, and service worker integration.
Offline Support
This configuration enables offline support by generating a service worker that caches your assets and pages, allowing your app to work offline.
const withPWA = require('next-pwa');
module.exports = withPWA({
pwa: {
dest: 'public'
}
});
Custom Caching Strategies
This configuration allows you to define custom caching strategies for different types of assets. In this example, images are cached using a 'CacheFirst' strategy.
const withPWA = require('next-pwa');
module.exports = withPWA({
pwa: {
dest: 'public',
runtimeCaching: [
{
urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/,
handler: 'CacheFirst',
options: {
cacheName: 'images',
expiration: {
maxEntries: 50,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
},
},
},
],
},
});
Service Worker Customization
This configuration allows you to customize the service worker behavior, such as enabling it only in production, skipping the waiting phase, and automatically registering it.
const withPWA = require('next-pwa');
module.exports = withPWA({
pwa: {
dest: 'public',
register: true,
skipWaiting: true,
disable: process.env.NODE_ENV === 'development',
},
});
workbox-webpack-plugin is a plugin for webpack that generates a service worker and precaches assets. It offers more granular control over caching strategies and service worker behavior compared to next-pwa, but requires more configuration.
next-offline is another Next.js plugin that helps you create a PWA. It provides similar functionalities to next-pwa, such as offline support and service worker integration, but it is less actively maintained.
sw-precache-webpack-plugin is a webpack plugin that generates a service worker using sw-precache. It is similar to workbox-webpack-plugin but is less feature-rich and is generally considered outdated in favor of Workbox.
This plugin is powered by workbox and other good stuff.
π Share your awesome PWA project π here
Features
next-i18next
example.module.js
when next.config.js
has experimental.modern
set to true
NOTE 1 -
next-pwa
version 2.0.0+ should only work withnext.js
9.1+, 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
.
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 next-pwa
Update or create next.config.js
with
const withPWA = require('next-pwa')
module.exports = withPWA({
// other next config
})
After running next build
, this will generate two files in your distDir
(default is .next
folder): workbox-*.js
and sw.js
, which you need to serve statically, either through static file hosting service or using custom server.js
.
If you are using Next.js 9+, you may not need a custom server to host your service worker files. Skip to next section to see the details.
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.
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.
Create a manifest.json
file in your static
folder:
{
"name": "PWA App",
"short_name": "App",
"icons": [
{
"src": "/static/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/static/icons/android-chrome-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/static/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 _document.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='/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' href='/static/icons/touch-icon-iphone.png'>
<link rel='apple-touch-icon' sizes='152x152' href='/static/icons/touch-icon-ipad.png'>
<link rel='apple-touch-icon' sizes='180x180' href='/static/icons/touch-icon-iphone-retina.png'>
<link rel='apple-touch-icon' sizes='167x167' href='/static/icons/touch-icon-ipad-retina.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='/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' />
<!-- apple splash screen images -->
<!--
<link rel='apple-touch-startup-image' href='/static/images/apple_splash_2048.png' sizes='2048x2732' />
<link rel='apple-touch-startup-image' href='/static/images/apple_splash_1668.png' sizes='1668x2224' />
<link rel='apple-touch-startup-image' href='/static/images/apple_splash_1536.png' sizes='1536x2048' />
<link rel='apple-touch-startup-image' href='/static/images/apple_splash_1125.png' sizes='1125x2436' />
<link rel='apple-touch-startup-image' href='/static/images/apple_splash_1242.png' sizes='1242x2208' />
<link rel='apple-touch-startup-image' href='/static/images/apple_splash_750.png' sizes='750x1334' />
<link rel='apple-touch-startup-image' href='/static/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' />
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.
const withPWA = require('next-pwa')
module.exports = withPWA({
pwa: {
dest: 'public'
}
})
Use this example to see it in action
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.
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('next-pwa')
module.exports = withPWA({
pwa: {
dest: 'public',
// disable: process.env.NODE_ENV === 'development',
// register: true,
// scope: '/app',
// sw: 'service-worker.js',
//...
}
})
false
disable: false
, so that it will generate service worker in both dev
and prod
disable: true
to completely disable PWAdev
, you can set disable: process.env.NODE_ENV === 'development'
true
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.basePath
in next.config.js
or /
/app
so that path under /app
will be PWA while others are not/sw.js
public
folder from being precached.
['!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.['!img/super-large-image.jpg', '!fonts/not-used-fonts.otf']
.next/static
(or your custom build) folder
[]
[/chunks\/images\/.*$/]
- Don't precache files under .next/static/chunks/images
(Highly recommend this to work with next-optimized-images
plugin)true
/login
, it's recommended to setup this redirected url for the best user experience.
undefined
/_offline
page such as pages/_offline.js
and you are all set, no configuration necessaryobject
fallbacks.document
- fallback route for document (page), default to /_offline
if 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 nonenext/link
on front end. Checkout this example for some context about why this is implemented.
false
""
- i.e. default with no prefix/subdomain
if the app is hosted on example.com/subdomain
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.
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!
{command: 'doSomething', message: ''}
object when postMessage
to service worker. So that on the listener, it could do multiple different tasks using if...else...
.clean application cache
to reduce some flaky errors.runtimeCaching
such as options.cacheableResponse.statuses=[200,302]
.sw.js
file to figure out what's really going on.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:
self.__WB_DISABLE_DEV_LOGS = true
in your worker/index.js
(create one if you don't have one).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.MIT
FAQs
Next.js with PWA, powered by workbox.
The npm package next-pwa receives a total of 138,423 weekly downloads. As such, next-pwa popularity was classified as popular.
We found that next-pwa demonstrated a not healthy version release cadence and project activity because the last version was released a year ago.Β It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
ESLint has added JSON and Markdown linting support with new officially-supported plugins, expanding its versatility beyond JavaScript.
Security News
Members Hub is conducting large-scale campaigns to artificially boost Discord server metrics, undermining community trust and platform integrity.
Security News
NIST has failed to meet its self-imposed deadline of clearing the NVD's backlog by the end of the fiscal year. Meanwhile, CVE's awaiting analysis have increased by 33% since June.