Building Chrome Extensions using Create React App

Dmytro Chumak
Dmytro Chumak
2 min read

Wrestling with Create React App feels like trying to teach a cat to swim. Let's sprinkle some fun into this tussle and configure it for a Chrome extension – because, why not add some spice to the mix?

TL;DR

Uncover the systematic process for configuring Create React App to seamlessly generate Chrome Extensions Manifest V3.

Github Repo

Introduction

This guide presumes a fundamental understanding of Chrome Extension development, allowing us to focus on generating a structure that encompasses the majority of features:

Structure

While HTML templates for the Background and Content script are unnecessary (being scripts in the Chrome Extension ecosystem), we will maintain a consistent structure. Also Devtools carries less significance here, but we'll incorporate it to demonstrate how to set up key features.

The main component here is the Popup, where we position our primary React app:

Chrome Extensions structure

Manifest.json

Manifest.json serves as the cornerstone file in the Chrome extension ecosystem. It informs the Chrome extension ecosystem about our intentions to connect the popup, background scripts, and dev tools:

{
"manifest_version": 3,
"name": "React Typescript Chrome Extension Boilerplate",
"version": "0.0.1",
"description": "Working React TypeScript boilerplate for a Chrome extension using Create React App.",
"background": { "service_worker": "background.bundle.js" },
"action": {
"default_popup": "./index.html",
"default_icon": "logo-34.png"
},
"icons": {
"128": "logo-128.png"
},
"content_scripts": [
{
"matches": ["http://localhost:8000/*"],
"js": ["content.bundle.js"]
}
],
"devtools_page": "devtools.html",
"web_accessible_resources": [
{
"resources": ["logo-128.png", "logo-34.png"],
"matches": []
}
],
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
},
"permissions": ["storage", "contextMenus", "scripting", "tabs"],
"host_permissions": ["http://localhost:8000/"]
}

With that, our next task is to modify the React Create App Webpack.

Webpack

We'll leverage react-app-rewired for this purpose. It assists in adjusting the create-react-app webpack config without the need to 'eject'. We'll add a couple of scripts in package.json:

"start": "react-app-rewired start",
"build": "BUILD_PATH=build react-app-rewired build"

Furthermore, we need to create a config-overrides.js file where we configure the structure.

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const pagesNames = ["Background", "Content", "Devtools"];
const CHROME_EXTENSION_OUTPUT_FOLDER = "build";
module.exports = {
webpack: (config) => {
config.plugins[0] = new HtmlWebpackPlugin({
...config.plugins[0].userOptions,
cache: false,
chunks: ["main"],
filename: "index.html",
template: path.join(__dirname, "src", "Popup", "index.html"),
});
return {
...config,
entry: {
main: [path.join(__dirname, "src", "Popup", "index.tsx")],
...pagesNames.reduce(
(acc, name) => ({
...acc,
[name.toLowerCase()]: path.join(
__dirname,
"src",
name,
"index.tsx"
),
}),
{}
),
},
optimization: {
...config.optimization,
runtimeChunk: false,
splitChunks: false,
},
output: {
...config.output,
chunkFilename: "[name].js",
filename: "[name].bundle.js",
path: path.resolve(__dirname, CHROME_EXTENSION_OUTPUT_FOLDER),
},
plugins: [
...config.plugins,
new webpack.ProgressPlugin(),
new CopyWebpackPlugin({
patterns: [
{
force: true,
from: path.join(__dirname, "src", "manifest.json"),
to: path.join(__dirname, CHROME_EXTENSION_OUTPUT_FOLDER),
transform(content) {
return Buffer.from(
JSON.stringify({
version: process.env.npm_package_version,
...JSON.parse(content.toString()),
})
);
},
},
],
}),
...pagesNames
.filter((name) => !["Content", "Background"].includes(name))
.map(
(name) =>
new HtmlWebpackPlugin({
cache: false,
chunks: [name.toLowerCase()],
filename: `${name.toLowerCase()}.html`,
template: path.join(__dirname, "src", name, "index.html"),
})
),
],
};
},
};

As a result, we should obtain a structure that satisfies Chrome's structural requirements.

Chrome Extensions Build

Voila! Let's build and load our extension now.

Loading the Chrome Extension

The final step is to load the build into the Chrome extension:

Chrome Extensions

Conclusions

As demonstrated, while React Create App might not be the most convenient environment to develop Chrome Extensions using React, it is certainly possible, but with a bit of complexity. Here's hoping you find this guide useful.

Do you like the article or have some remarks? Let's discuss it!

Join the Newsletter

Subscribe to be up to date by email.
Cool content and no spam.

tridenttridentGlory to Ukraine!