Currently, there’s no way to add a custom service worker with the Workbox InjectManifest plugin in create-react-app (2.x). There are a few solutions floating around, but if you don’t want to eject, here’s a simple way to add Workbox to your Create-React-App (2.x) workflow.
The Issue: This PR would allow adding custom configurations to the built-in service worker that uses GenerateSW
GenerateSW
InjectManifest
TL;DR – This involves not using the SW generated by Create-React-App but generating your own SW. The SW is configured to use the InjectManifest plugin and the Workbox build workflow. Build commands are added to the react-build script in package.json.
Step 1: Make sure you register for a SW
in the main index.js
file of your app
Register for a Service Worker in CRA by editingsrc/index.js
and then change serviceWorker.unregister();
to serviceWorker.register();
This tells CRA to look in the src/serviceWorker.js
, and load the default Service Worker.
Step 2: Tell CRA to Load a Custom Service Worker
Next, src/serviceWorker.js
export function register(
config
).
Here, you’ll find the load
event listener (around line 34) window.addEventListener('load', () => {
which provides the reference to the Service Worker for
You need to change swURL
So, change this – const swUrl = ``${process.env.PUBLIC_URL}/service-worker.js``;
to const swUrl = ``${process.env.PUBLIC_URL}/sw.js``;
window.addEventListener('load', () => {
// const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
// Add a custom service worker until CRA team provides a way to inject config into workbox.
const swUrl = `${process.env.PUBLIC_URL}/sw.js`;
if (isLocalhost) {
Step3: Create the Custom Service Worker Template and Build Files
Now, create two files under src/
– sw-build.js
and sw.js
sw-build.js
– Provides the build instructions for the custom Service Worker generation.
sw-template.js
– Is the template for Workbox Service Worker. Custom caching rules go here.
Step 4 – Add the Service Worker Build Instructions with InjectManifest Mode
In src/sw-build.js
add the build instructions as provided by
Google’s injectManifest example here.
const workboxBuild = require('workbox-build');
// NOTE: This should be run *AFTER* all your assets are built
const buildSW = () => {
// This will return a Promise
return workboxBuild.injectManifest({
swSrc: 'src/sw-template.js', // this is your sw template file
swDest: 'build/sw.js', // this will be created in the build step
globDirectory: 'build',
globPatterns: [
'**\/*.{js,css,html,png}',
]
}).then(({count, size, warnings}) => {
// Optionally, log any warnings and details.
warnings.forEach(console.warn);
console.log(`${count} files will be precached, totaling ${size} bytes.`);
});
}
buildSW();
In the code snippet above, you’ll notice swSRC
sw-template.js
injectManifest
swDest
build
Step 5 – Provide Custom Caching Rules in src/sw-template.js
You now need to add the injection point and caching rules for Workbox. Workbox will load these from src/sw-template.js
Add the following to src/
sw
-template.js
if ('function' === typeof importScripts) {
importScripts(
'https://storage.googleapis.com/workbox-cdn/releases/3.5.0/workbox-sw.js'
);
/* global workbox */
if (workbox) {
console.log('Workbox is loaded');
/* injection point for manifest files. */
workbox.precaching.precacheAndRoute([]);
/* custom cache rules*/
workbox.routing.registerNavigationRoute('/index.html', {
blacklist: [/^\/_/, /\/[^\/]+\.[^\/]+$/],
});
workbox.routing.registerRoute(
/\.(?:png|gif|jpg|jpeg)$/,
workbox.strategies.cacheFirst({
cacheName: 'images',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
}),
],
})
);
} else {
console.log('Workbox could not be loaded. No Offline support');
}
}
Now, all you need is to add the build instructions for the custom Service Worker in package.json
of your React application.
Step 6: Install the workbox-build
dependency
Install workbox-build
dependency by running
npm install workbox-build --save-dev
Note: workbox-build may already be included in your node_modules, but running the above will also make an entry in your package.json, and
Step 7: Add the Workbox Build Instructions for CRA
You now need to specify the Workbox build step package.json
To do this, edit package.json
and add a new entry in the scripts
section – "build-sw": "node ./src/sw-build.js"
This will run a node command to build the custom service worker using the instructions from src/sw-build.js
Next, change the default React build command: "build": "react-scripts build
to "build": "react-scripts build && npm run build-sw
This is how it should look –
},
"scripts": {
"start": "react-scripts start",
"build-sw": "node ./src/sw-build.js",
"build": "react-scripts build && npm run build-sw",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
Final Step – Cleanup Default CRA Service Worker
When your npm run build
sw.js
build
The default CRA service-worker.js
build
},
"scripts": {
"start": "react-scripts start",
"build-sw": "node ./src/sw-build.js",
"clean-cra-sw": "rm -f build/precache-manifest.*.js && rm -f build/service-worker.js",
"build": "react-scripts build && npm run build-sw && npm run clean-cra-sw",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
/**
* UPDATE
* thanks to @do in the comments
*
* in sw-build.js you need to update the glob patterns before adding clean-cra-sw,
* else the files removed will be added to the precache of workbox.
*/
globPatterns: [
'**\/!(service-worker|precache-manifest.*).{js,css,html,png}',
],
That’s it. When you run npm run build
your React app will now use your custom Service Worker!
You can also follow this issue on Github for Guidelines to use Workbox with Create-React-App (2.x)
Vagner Nicolodi says
Congratulations for your tutorial!
Tried lot’s of things on the internet and no one worked.
Mithun says
Why is file `build/precache-manifest.*.js` being removed? It appears to result in an error when deployed to production (bad precaching response / 404)
Aaron Tomlinson says
When I try to deploy this to Heroku it shows:
> app@0.1.0 build-sw /tmp/build_3beecdaee89f062441116cecdccf909c
remote: > node ./src/sw-build.js
remote:
remote: 7 files will be precached, totaling 127306 bytes.
However I do not see any evidence of the workbox in the console.
I applied this method to a brand new create-react-app.
Robbert says
Hi Karan,
Thanks a lot for this explanation, I’d be interested if you could also explain how to invalidate the service worker when a file (say Javascript) has changed. I am using the setup you explain above but the service worker is not picking up new files, until I invalidate the sw.
D.GJ says
Thanks for this valuable guide.
I have one questions, I have a typed script CRA and was wondering how should I add those files, as ts or js? Does that matter
Mehk says
This has been very helpful to me, thanks! But is there also a way to invalidate the serviceworker? when the codebase has been changed? because right now its caching the files and code changes are not being shown unless I clear storage or visit with a browser that has never been there before…
Shannon I'Ons says
Can you confirm whether this solution works for CRA 3?
Nikolay says
Hi,
First, thanks for the great tutorial!
Now I am new to PWA, so excuse me if this is not relevant:
How do I subscribe for the push service? Is it a feature in workbox or do I need to do some workaround?
Bindushree Udevasa Vishwanath says
Hi,
Getting error when using” rm -f build/precache-manifest.*.js”
> devias-material-kit-pro@1.3.0 clean-cra-sw C:\code\CVA\ReactApp
> rm -f build/precache-manifest.*.js && rm -f build/service-worker.js
‘rm’ is not recognized as an internal or external command,
operable program or batch file.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! devias-material-kit-pro@1.3.0 clean-cra-sw: `rm -f build/precache-manifest.*.js && rm -f build/service-worker.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the devias-material-kit-pro@1.3.0 clean-cra-sw script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\bindu\AppData\Roaming\npm-cache\_logs\2019-11-18T03_27_37_653Z-debug.log
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Nish Achchige says
try…
“clean-cra-sw”: “rimraf ./build/precache-manifest.*.js && rimraf ./build/service-worker.js”
instead…
“clean-cra-sw”: “rm -f build/precache-manifest.*.js && rm -f build/service-worker.js”
more info: https://stackoverflow.com/questions/55786102/why-to-use-rimraf-dist-command-in-build-script
do says
in sw-build.js you need to update the glob patterns before adding clean-cra-sw else the files removed will be added to the precache of workbox.
globPatterns: [
‘**\/!(service-worker|precache-manifest.*).{js,css,html,png}’,
],
Daniel T Ichiyama says
Seems pretty cool. Any follow up post about how to build out the sw-template?
Nicolas Kumnick says
Thanks a lot for this great solution!
Perfect solution to get away from rewired!
Robert says
Very helpful tutorial, I use this for my students. One remark:
Workbox has changed from V4 to V5 recently. In the process the injection point for manifest files has to be changed from
“`js
workbox.precaching.precacheAndRoute([]);
“`
to
“`js
precacheAndRoute(self.__WB_MANIFEST);
““
Otherwise sw-build will throw an exception.
See also https://developers.google.com/web/tools/workbox/guides/migrations/migrate-from-v4
Matt A says
Great tutorial! Thank you for the valuable info!
I was receiving the following error upon running npm run build:
> node ./src/sw-build.js
(node:4187) UnhandledPromiseRejectionWarning: Error: Unable to find a place to inject the manifest. Please ensure that your service worker file contains the following: self.__WB_MANIFEST
I resolved it by editing sw-template.js on line 10 to the following:
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);
Not sure if anyone else is running to this issue but thought I would share!
Kim says
Thanks for the tutorial! Any thoughts on this error?
“Error: Unable to find a place to inject the manifest. Please ensure that your service worker file contains the following: self.__WB_MANIFEST”
Kaywee says
While changing the service worker to custom service worker, the instructions you have given show to create sw.js once and sw-template.js once, but the image that you provided shows sw-template.js. I feel it is a mismatch. Which should we follow?