By Zaid Humayun
This is a follow up to my previous post about building a PWA with create-react-app (CRA). In the linked post, I discussed how we could go about building a custom Service Worker (SW) while staying within the create-react-app shell.
If you followed along with the post (and hopefully got it working), you’d have noticed one critical flaw. It is still extremely hard to develop a SW in a dev environment. Essentially, you’d have to modify your SW code, run a build process, test it out, iron out any bugs, refresh and repeat. Speaking from experience, it is an exhausting process.
Let’s go ahead and figure out how to solve that problem.
Working In Dev Mode
Okay, so how do we get a SW running in dev mode, so we can quickly write some bad code, and figure out what works and what doesn’t?
First, let’s figure out why it doesn’t work in dev mode. Create a fresh CRA project, and open up the registerServiceWorker.js
under the src
directory.
In the above gist, I only have the relevant piece of code. You will notice a conditional check process.env.NODE_ENV === 'production'
. This is checking to see if you are running a production build. If you aren’t running a production build, the SW will be replaced by a no-op file.
The reasoning behind this decision is provided in this GitHub issue.
First, try and run yarn start
on your app and check for a SW file in the toolbar window. If you click on the service-worker.js
link in the toolbar, you will be shown the following file:
Fortunately, there is a simple fix for this. It’s an easy two-step process.
First, inside of the registerServiceWorker.js
file, look for the window.addEventListener('load')
function call. The first line is a declaration for swUrl
which says:
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
Rename the service-worker
part of it with anything else. I’m going to name mine service-worker-custom.js
.
Second, create a file inside of your public directory with the exact same name as the custom name you just came up with. So, I would create a file called service-worker-custom.js
inside of the public directory.
Now, inside of the service-worker-custom.js
, place a simple log statement. Something like: console.log('My custom service worker')
.
Now, run your app again with yarn start
and you should see the log statement pop up in your browser console. You might need to unregister a previous service worker if you ever ran yarn start prior to this.
So, there you have it. A custom service worker that you can run safely inside of dev mode.
Note: It is unwise to test a service worker in a dev env outside of private browsing mode in your browser. Also, always make sure Update On Reload is checked inside your dev tools window when testing in dev mode.
Combining Dev and Prod
Now, we’ve figured out how to test a SW in dev mode. However, we also need to find a way to inject our custom code into the SW generated by CRA in a production build.
If you keep everything as is with the configurations we’ve made so far and run a build process and check the build in your browser, you will notice that the SW file generated is the custom one we created. This is a problem, because we want to be able to combine the goodness of what CRA has to offer us with our own code.
We can do this with thesw-precache
library. I introduced this library in my previous post. Here is the GitHub link to the sw-precache
library.
Install the library with yarn add sw-precache
. Once you’ve done that, create a sw-precache-config.js
file in your root directory. Here is my file:
I’ve introduced most of this file in the previous post. The only new bit is the importScripts
option. This is fairly self-explanatory, it simply imports the file specified by the path, and we are trying to import our custom SW file.
You will notice that the path of the file lacks the ./public
prefix, despite the file being present in our public
directory. I’ll explain this in a bit.
Now, update your package.json
file with a modification to the build
command. Make your build
command the following:
react-scripts build && sw-precache --config=sw-precache-config.js
Now, let’s go back to the file path we provided to the importScripts option. If you notice, the sw-precache
is essentially running as a post build process. Now, if you just run a build process, and open up the build directory that is created, you will notice your custom service worker file in the build folder. When we provide a path to the importScripts
option, we are providing it relative to the build directory!
Once, you’ve done all of that, go ahead and run the build version of your app, and you will notice that the log statement pops up once again in your browser console.
Well, there you have it! You can now inject some custom SW code into the default SW generated by CRA!