React starter kit for Chrome Extensions with Live Reloading 🤓
May 07, 2019 ∙ 📓 5 min
Software is eating the world. And JavaScript is eating software. Chrome extensions are not an exception here. They are also built using JavaScript. A few days ago, I got an opportunity to work on a small side project which needed a chrome extension. Since I am new to ReactJs and want to tame it, so I decided to build the desired extension with it. Before I narrate the whole story, let's take a gander on how one develops a chrome extension.Chrome extension overview
Every chrome extension starts with a manifest.json
file which provides an overview of the permissions, background scripts, content scripts, popups and many other things used by the extension. Here’s a sample manifest file.
{
"name": "Getting Started Example",
"version": "1.0",
"description": "Build an Extension!",
"permissions": ["storage"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"page_action": {
"default_popup": "popup.html",
"default_icon": {
"16": "images/get_started16.png",
"32": "images/get_started32.png",
"48": "images/get_started48.png",
"128": "images/get_started128.png"
}
},
"manifest_version": 2
}
Folder/bundle containing a manifest file, scripts, and other assets, is loaded into chrome from chrome://extensions/
page. Chrome reads the manifest file and installs the extension. If any change is made in script or any other file, we reload the extension to install the latest change.
Here’s a link to the official documentation which elborates chrome extensions and their installations.
After getting familiar with the basics of extension development, I put on my developer hat 🤠 and started building an extension with create-react-app. Here are the steps I followed:
- Created a react app by runnning
create-react-app hello-extensions
- Replaced
manifest.json
file with below content.
{
"name": "Hello Extension",
"description": "My first chrome extension",
"version": "1.0",
"manifest_version": 2,
"browser_action": {
"default_popup": "index.html"
},
"content_security_policy": "script-src 'self' 'sha256-5As4+...'; object-src 'self'"
}
- Build the app using
npm run build
- Loaded the build in chrome.
…and it worked. Yay!!
My “Hello World” moment with extension made me happy but my happiness didn’t last for long when I decided to change the background color. I found out that I have to build and reload the extension again 😩.
Build changes 🤖
Running an app(created by create-react-app
) in development mode with npm run start
doesn’t write files to the disk. I needed files to be present on the disk so that they can be loaded in chrome. As we know, the create-react-app
utility uses webpack under the hood with a predefined configuration. And to customize that predefined config we must eject it first. So I ejected the config with the command npm run eject
. It generated some config files and scripts.
After ejecting the configuration, I needed to find a way to write files to disk in development mode. I ran to the webpack dev server api document for help and found an option writeToDisk
. As its name suggests, setting writeToDisk
true will write files to disk. So I set it true
in the dev server config. It helped but it took a toll on the dev server. Dev server became sluggish. Its sluggishness convinced me to look for a better alternative.
I thought for a while and realized that I didn’t need a dev server. I needed a “watch server” 😅. A server to watch files and copy them—with the latest changes—to build directory on the change
event. I leveraged webpack.watch
api and ended up with the watch script below.
// some neccessary setup code
...
...
...
// Webpack watch
webpack(config).watch({}, (err, stats) => {
if (err) {
console.error(err);
} else {
copyPublicFolder();
}
console.error(
stats.toString({
chunks: false,
colors: true
})
);
});
function copyPublicFolder() {
fs.copySync(paths.appPublic, paths.appBuild, {
dereference: true,
filter: file => file !== paths.appHtml
});
}
Reload changes 🔄
After dealing with the build barrier, the next challenge was to automatically reload the extension. I found a supercool webpack plugin webpack-chrome-extension-reloader which reloads chrome extensions out of the box. A million thanks to Rubens for writing it. A sneak peek of config with the plugin.
// Other configuration
...
...
...
...
plugins: [
// Other plugins
...
...
...
// Chrome extension hot reloading.
isEnvDevelopment &&
new ChromeExtensionReloader({
reloadPage: true // Force the reload of the page also
entries: {
// The entries used for the content/background scripts
contentScript: 'content-script',
background: 'app' // *REQUIRED
}
})
].filter(Boolean),
Illustration of the starter kit with live reloading.
Here’s the link to complete package/starter kit. I hope it will be helpful. If you have any idea/suggestion to make it better, please do share. Thanks 🙏🏻. Happy Coding!
Hi, I am Hitesh.