If you are used to creating Chrome extensions, you don’t have to read this, you can simply get the code. I wanted to expose a few problems I went through while creating this starter project.
🚧 Create the Foundations
Open your terminal on the folder you want to use and
init your project with the following command:
$ npm init -y
Create the first folders and files:
$ mkdir src && cd src && mkdir assets background contentscript
$ cd background && touch background.ts
$ cd ../contentscript && touch contentscript.ts contentscript.scss
Install the first batch of dependencies:
$ npm i -D @types/chrome @types/node awesome-typescript-loader css-loader mini-css-extract-plugin node-sass sass-loader ts-node tslint typescript email@example.com webpack-cli
⚠️Note that, at the time of writing,
Create-react-appuses firstname.lastname@example.org and we have to use the same webpack version to successfully compile our app later. (Feel free to install the last version of Webpack to reproduce the bug and try to find a solution)
📦 Set Up webpack
In the root directory, create
$ touch tsconfig.json webpack.config.js
Fill the files as below:
I won’t explain what we are doing here, it’s a simple webpack configuration and pretty much self-explanatory.
We are telling webpack to get our
.scss files to
Let’s create a script in our
package.json to use this Webpack configuration.
npm run watch and
npm run build should generate some files in the
Great! So far, we created a development and a production mode for our background and content scripts.
📋 Adding Manifest.json
manifest.json will tell Chrome what to do with all our files and define some capabilities of our extension.
Let’s create it in the root directory.
$ touch manifest.json
Fill it with this content:
./dist folder will be the folder that we will import to Chrome later.
It needs to always have a copy of the
manifest.json. To do this, we are going to add three new scripts to our
cleanscript will clean the
./distfolder and provide it with a fresh copy of our
manifest.json. And, as we are here, the script will also copy our
./assetsfolder to the
prewatchscripts will be called every time we run the watch or build script (by doing
npm run ...) and before the actual script is executed.
As you can see, I’m using the cpy-cli package to copy files. Don’t forget to install it:
$ npm i -D cpy-cli
🎟 Import to Chrome
Let’s try to import our extension to Chrome.
Run the watch script (
npm run watch) and when the code is compiled, open Chrome.
In Menu > More tools > Extensions, click on Load Unpacked and select our
Your extension should be added to Chrome and if you’ve added some content to the
contentscript.ts files (just
console.log some stuff), you should see them in their respective consoles.
💣 Create the React-Based Popup
We are going to use create-react-app for simplicity’s sake.
If you don’t have it, install the package. Go to your
./src folder and create-react-app.
⚠️Please, use popup as a name, we are going to use this name later.
$ npm i -g create-react-app && cd src && create-react-app popup --typescript
ℹ️The last version of this package allows us to give a
-- typescriptflag so we have a TypeScript-ready application.
We will encounter our first issue here.
We need to put the compiled React code in the
./dist folder of the root directory. We can do that easily, but if we have to use
npm run build every time we change a line of code to see if it works, it will be pretty tedious.
We can’t really use
npm run start either, because we don’t have access to generated files so we cannot put them in the
To be able to compile and watch our files at the same time, we are going to use a package named cra-build-watch.
In your terminal, go to the root directory and install that package:
$ npm i -D cra-build-watch
Now, open the
package.json of the newly created React app (not the one in the root directory).
Add the following script, as well as the homepage’s settings (to make sure all the asset paths are relative to
Once again, open the
package.json file in the root directory and add the following script which will be used to watch the file’s modification of our React app.
ℹ️Notice the prefix flag that allows us to go to another directory (with another
If you did everything well and tested with
npm run watch in the main directory, the script should now compile your background, your content script, your React’s popup, and put everything in the
Add one last script to the
package.json, that will allow us to build our React popup:
As well as building the app, it will copy it to the
./dist folder of the root directory.
We have to update our Chrome extension’s
manifest.json to let it know we now have a popup:
Now, you can
npm run build and add the extension to chrome as before.
If you click on the extension’s icon, you will see a white square… And not our promised React popup.
Do a right-click on the white square, inspect, and open the developer console.
You will see this error:
That’s our last issue. What’s happening here?
Chrome blocks inline scripts in extensions. The build version of our react-app does have some inline script in the compiled
As written in the console, one of the workarounds is to copy the SHA in this message and add it to the
“content_security_policy” setting as below:
npm run build again and reload your extension in chrome. If you click on the icon of your extension, you should see our React app loading as on this screen:
🎁 Zipping Your Extension
If you want to use a script to zip your extension, install the bestzip package and add the following script to your main
$ npm i -D bestzip
That’s it for today! Thank you for reading, I hope you’ve learned some things. You now have a Chrome extension, ready to be used with TypeScript and React!
🏎️ Get the full code here.