DEV Community

loading...
Cover image for use create-react-app to develop Chrome extensions

use create-react-app to develop Chrome extensions

jamalx31 profile image Jamal Mashal Originally published at jamalx31.com ・3 min read

create-react-app (CRA) is probably the most common way to build, develop, and deploy React apps. A couple of weeks ago, I was working on a chrome extension. I wanted to use CRA to do it, but CRA only supports SPA out of the box.

I found the article Create Chrome Extension in React by @bayardlouis470 , which I used to develop my extension, but there are 3 main issues with his approach:

  1. It doesn't support multiple pages (if you want to have options page and popup for your extension)
  2. You only use the production build version which makes it harder to debug.
  3. After every change in your code, you need to re-run the build cmd

So I decided to take it a step further. Here will be sharing how to customize CRA to fit our needs to develop a chrome extension (a link to the full code at the end).

Step 1: ⚛ create a react app

npx create-react-app extension

Step 2: Modify public/manifest.json

You already have a manifest file created by CRA but we need to change it to match the extension requirements

{
  "name": "Awesome Extension",
  "version": "0.0.1",
  "manifest_version": 2,
  "description": "create-react-app for extensions",
  "icons": {
    "128": "logo128.png"
  },
  "permissions": [],
  "background": {
    "scripts": [
      "background.js"
    ],
    "persistent": true
  },
  "browser_action": {
    "default_icon": "logo128.png",
    "default_popup": "popup.html"
  },
  "options_page": "index.html"
}

Notice that we have index.html for the options page, and popup.html for our extension popup.

Step 3: 🏗 Structure

inside ./src

Go ahead and create (inside src) a folder for the options and one for your popup. Also, create your background.js file.

inside ./public

duplicate the index.html file and rename it to popup.html

inside ./

create .env file and add the following

INLINE_RUNTIME_CHUNK=false

This is important since chrome doesn't allow inline script js code

Step 4: 🎩 The magic

Now we need to customize CRA. For this we will be using 4 great packages:

  1. customize-cra
  2. react-app-rewired
  3. copy-webpack-plugin
  4. react-app-rewire-multiple-entry

1 & 2 to override CRA webpack default configurations. 3 to copy our static assets and 4 to support multiple pages. so go ahead and install them.

npm -i customize-cra react-app-rewired copy-webpack-plugin react-app-rewire-multiple-entry --save-dev

Now where all the magic happens. Create config-overrides.js in your root folder with the following code

const {
   override,
   overrideDevServer,
   addWebpackPlugin
} = require("customize-cra");
const CopyPlugin = require('copy-webpack-plugin');

const multipleEntry = require('react-app-rewire-multiple-entry')([
   {
      // points to the popup entry point
      entry: 'src/popup/index.js',
      template: 'public/popup.html',
      outPath: '/popup.html'
   },
   {
      // points to the options page entry point
      entry: 'src/options/index.js',
      template: 'public/index.html',
      outPath: '/index.html'
   }
]);

const devServerConfig = () => config => {
   return {
      ...config,
      // webpackDevService doesn't write the files to desk
      // so we need to tell it to do so so we can load the
      // extension with chrome
      writeToDisk: true
   }
}

const copyPlugin = new CopyPlugin({
   patterns: [
      // copy assets
      { from: 'public', to: '' },
      { from: 'src/background.js', to: '' }
   ]
})

module.exports = {
   webpack: override(
      addWebpackPlugin(
         copyPlugin
      ),
      multipleEntry.addMultiEntry,
   ),
   devServer: overrideDevServer(
      devServerConfig()
   ),

};

To make everything play together we just need to modify the scripts in package.json to use react-app-rewired and it will look like this:

"scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  }

Step 5: 🎉 Run & enjoy

Like any other CRA project, run the development cmd with

npm run start

Wepback will create a dist folder Load it as an unpacked extension in Chrome. And when you are ready to publish your extension you can use the build command.

if everything went as planned your popup will look like this. And the best part is if you change your code you will see it instantly 🥳

Alt Text

Source code

I published the full boilerplate of on GitHub, so you can check the code if you'd like or just clone to develop your own extension.

Final thoughts

Room for improvements:

  • minimize the background.js file
  • optimization
  • use webpack-extension-reloader?

Let me know if you have any questions, thoughts or suggestions!

Discussion

pic
Editor guide
Collapse
fly profile image
joon

Recently had idea for an extension and was pondering on how to begin, thank you for the post! It was a very much needed reminder :)

Collapse
jamalx31 profile image
Jamal Mashal Author

Glad to hear that!

Collapse
miso01 profile image
Michal Švec

Really amazing article, good work :) ! On the internet are few solutions for react extension but almost every of them have some drawbacks. This seems to me be the best solution and the most explained solution.

I have one question for you. How can I include content script into manifest ? I've tried it to put it to the same folder as background.js and when I try to load it I get an error which saying that unable to load content.js.

"content_scripts": [
{
"matches": [""],
"js": [
"content.js"
]
}
]

I found one way which works, but it's quite complicated, you know... It's needed to eject react and remove some configs which hashing the name of file after every build. Then I loaded file from static folder....

"content_scripts": [
{
"matches": [""],
"js": [
"/static/js/2.chunk.js",
"/static/js/main.chunk.js",
"/static/js/runtime-main.js"
]
}
]

I'm quite confused why background.js can be loaded from same folder and content.js can't.

Collapse
jamalx31 profile image
Jamal Mashal Author

Hi Michel,
I think you can do that by modifying config-overrides.js to copy your js file the same way I copy background.js

Collapse
aayushidroid profile image
Aayushi Sharma

React is cool. I am new to react.

Collapse
jamalx31 profile image
Collapse
moatazabdalmageed profile image
Moataz Mohammady

I was thinking how can I add react to chrome extension ...many thanks from Egypt

Collapse
tilakmaddy profile image
Tilak Madichetti

how do I get control of the html .coz I am trying to make an extension that replaces all images of a page with cat pics api

Collapse
jamalx31 profile image
Jamal Mashal Author

You need to use Content Scripts. if you follow this tutorial you can better understand how it's done