Creating a Chrome extension in React is no different than creating one in vanilla JavaScript. After all, it's all JavaScript in the end. But we just need to keep few details in our mind. In this post, let's see how to create a simple Chrome extension with React.
1. Creating and setting up a React application
For this, we are going to use create-react-app
tool, as it makes it easy to get started with React. In your command line, go to your workspace directory and run npx create-react-app chrome-react-extension
. This will setup a sample React application named "chrome-react-extension", with all the build steps built in.
Once you have the basic react app created, run yarn start
to make sure the application is working fine. If all good, you will see a browser page, loaded with the React app.
Now, let's move on to configuring this app to make this as an Chrome extension.
2. Configuring React app to work as a Chrome extension
A Chrome extension requires a manifest.json
file to be present in root directory of the extension. Luckily, our create-react-app
already has one. We just need to add few details in it to make it compatible with Chrome's manifest.json
spec.
Open the file [PROJECT_HOME]\public\manifest.json
and add the following code to it. The file should already contain other entries and that's fine, just add the code at the end of it.
{
/* ... other values */
"version": "1.0",
"manifest_version": 2,
"browser_action": {
"default_popup": "index.html"
}
}
Also, the file will have some default entries for icons
, but this is not compatible with the Chrome's manifest.json
format. You can either update this to comply with "icons" format or just remove the entry as we do not need it for our extension to work.
And, that's it. That's all you need to do to get out React app working as a Chrome extension. Build this app as you normally build a react app with yarn build
. This generates the app and place the files inside [PROJECT_HOME]\build
.
Now, let's install it in Chrome.
3. Adding React app extension to Chrome
In Chrome, go to chrome://extensions
page and switch on developer mode. This enables the ability to locally install a Chrome extension.
Now either click on the LOAD UNPACKED and browse to [PROJECT_HOME]\build
or just drag and drop the build
folder. This will install the React app as a Chrome extension.
Yay! We just installed our React app as a Chrome extension. When you click the extension icon (the auto generated one), you will see the React app, rendered as a extension popup.
4. Making changes and debugging the extension
You can continue to make changes to your React app and do a build. To refresh the changes in your browser, just click on the Reload
button on your extension's tile in chrome://extensions
page. This will automatically pull changes from your build folder and re-install the extension.
And, to debug the extension in browser, you can either right click the extension icon and select Inspect popup or once the popup is opened, right click in it and select Inspect. This wll open the developer console, where you can inspect console logs, network traffic, local storage, etc.
5. Injecting a React app to page as content script from Chrome extension
So far we used React to render the extension popup. For some extensions, you may also need to render some UI to any underlying page. Chrome extension uses content_scripts
for this. Using content_scripts
, you can mention a JS and CSS file in manifest.json
, that needs to be injected` into the underlying page. Then this script will have access to the page DOM and can do the normal DOM manipulation.
But the problem with our create-react-setup
is that the build step will generate the output JS file in different name each time (if the content changed). So we have no way to know the actual file name of the JS file, hence we can't mention in it in our manifest.json
file.
To workaround this problem, you can always add a static JS file under the public
folder and when built, this JS fill will be part of the build
folder. Then you can use this file name as your content_script
entry point. But then, this file will not be part of React / Webpack build pipeline so you either have to setup your own build or settle for a vanilla JS in here.
As another workaround, you can just eject out of create-react-app
and modify the webpack configuration by hand to either retain the generated file name or create a separate entry point for content script.
Let's do this.
6. Ejecting create-react-app and configuring content scripts
First run yarn run eject
on the command line. This will eject create-react-app
and then will create all necessary build scripts inside your project folder. Once ejection is done, go to [PROJECT_HOME]\config\webpack.config.prod.js
file and make the following changes in it:
Change the option entry
to have multiple entry points. Here, our content script will be named as content.js
.
entry: {
app: [require.resolve('./polyfills'), paths.appIndexJs],
content: [require.resolve('./polyfills'), './src/content.js']
},
Also, Search for .[contenthash:8]
and remove it from both CSS and JS output file name. This will ensure the generated file will not have a random hash in it, thus we can mention the file name in our manifest JSON.
Once you made the above changes in the webpack.config.prod.js
file, now its time to create the content script file. Create a file named content.js
and content.css
inside the src
folder.
/* src/content.js */
import React from 'react';
import ReactDOM from 'react-dom';
import "./content.css";
class ContentReact extends React.Component {
render() {
return (
<div className={'react-extension'}>
<p>Hello From React Extension!</p>
</div>
)
}
}
const app = document.createElement('div');
document.body.appendChild(app);
ReactDOM.render(<ContentReact />, app);
/* src/content.css */
.react-extension {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: grid;
align-items: center;
justify-items: center;
background: rgba(0,0,0,.4);
}
.react-extension p {
background-color: #fff;
padding: 20px;
border-radius: 5px;
}
Now that we have configured React build pipeline and created our content scripts, lets update manifest.json
to pick up these files. Add the following code to manifest.json
file.
"content_scripts" : [
{
"matches": ["https://reactjs.org/*"],
"css": ["/static/css/content.css"],
"js": ["/static/js/content.js"]
}
]
Note that how we refer the JS and CSS file. Also, with matches
option, we are injecting this content script only to the React JS website. But you can inject any website you want, just by adjusting the matching pattern.
Now build your app, go to chrome://extensions
and reload the extension. After this, when you go to https://reactjs.org website you should see our extension injected model there.
This brings the end of this article. Feel free to let me know if there are questions.