Chrome Extensions in React + Redux

Pier Roberto Lucisano
Codewords
Published in
6 min readJan 6, 2018

This article aims at explaining in a clear and understandable way how to set up a Chrome Extension using React and Redux. For the very basic steps I followed this article which is a boilerplate updated and complete of a Chrome Extension with React.

I assume that you already know what is a content script, a popup and a background page. If you do not, you can give a look at the Chrome Developer documentation. If you have already heard about these concepts, the following picture should help you to remember the concept of background and content script.

Basic structure of a Chrome extension

For this example I use a Chrome Extension which I developed recently. If you are interested in what it does you can give a look at this article or download it. It will be helpful to understand the next sections of the tutorial. Here you can find also the repo of the project. I recommend you to follow this article together with the project I linked.

What you need

For this example you need a background page, a content script and a popup. Unfortunately popups are not persistent in Chrome Extensions. Indeed every time you close the popup, all the information is lost. This is a reason why you want to implement React on a Chrome Extension.

Furthermore, every time you close the window and therefore every time you quit the browser, the information kept in the state of React is lost as well. In order to get rid of this problem I use the local storage.

The best way to exploit the potential of the Chrome Extensions structure is to consider the background page as the store of your app. The reason is that the background page is persistent and everything is kept until the browser runs. Therefore, you can consider React components as part of the popup, and the background page as the store.

Set up manifest.json

The following snippet is the manifest.jsonwhich is the very first thing you need to consider when you develop a Chrome Extension. In this example I have a content script, a background page and a popup.

manifest.json

You can customise the file manifest according your needs. In this example I need a content script in order to show a button at the bottom of the page to save the tab. The best practise is to load the content script at the end of the document, this is the reason why I would suggest to use "run_at": "document_end". Remember to specify in which pages you want the content script to appear. If you do not specify matches , the content script does not load in any page.

Webpack configuration

The Webpack configuration I use is the following and this is the one I recommend for a Chrome Extension in React + Redux.

In order to compile your project run npm run build . This generates thedist folder which you need to upload in order to see the changes of the extension directly in your browser.

Now you can create the folder src plus a nested folder called pages. Inside it you have the core of the extension which contains three folders: background, content and popup. In the next sections, I analyse each of them.

Remember that you need babel as well, indeed most of this code cannot be read by the browser without it.

Background

In this folder we add localstorage.js, actions.jsand reducers.js. In this example I use the local storage but you could implement a backend as well. The main difference is that, as the name suggests, the local storage stores the information in your computer, while through the backend you can get the access to the information from every device. In order to get the access to the local storage of your Chrome Extension, you need to inspect your popup and then click on Application. A screen like this should appear:

Considering the example, index.js calls store.js which connects the background page with the store. Indeed, the first thing I do is to create the store, and this should be quite familiar to you. loadState()comes from the localstorage.js and basically it checks whether the local storage of browser has any data available. store.subscribe(...) saves in the local storage the new information added. I use the throttle function from lodash as well in order to prevent the user of clicking many times on the button and make the extension to crash. Finally, I wrap the store:

wrapStore(store, {  portName: 'COUNTING' })

You just need to keep in mind the port name which can be anything you want but it has to be the same when we use it in the next sections.

Actions and Reducers are set in the same way you do when you use Redux.

For the local storage configuration, I followed this tutorial by Dan Abramov. It takes only 8 minutes. You can watch it or just give a look to my code in localStorage.js which is basically the same.

Finally, a quick note about debugging the background. If in the code you add some console logs, you can read them from settings > more tools > extensions and then by clicking on pages/background.html of the extension which I assume you have already loaded.

Popup

In the popup folder there are all the components. The very interesting thing here is the index.js page which uses a third library, react-chrome-redux created by a startup called GoGuardian. Here you can find the video and the article where the library is explained. It is really useful and I recommend you to watch it carefully. The documentation of the library is not complete tough.

The main difference compared with a React app is that you need to create a bridge in order to connect the popup page to the background page. This is possible through the following line of code:

const store = new Store({  portName: 'COUNTING' });

Therefore, after the store is loaded, I mount my App component together with the Provider in order to get access to the props.

A quick note about the debugging. If you need to get access to the console, you have to open the popup, then right click on it and inspect the page.

Content

In this folder you need to have an index.js file as for the previous folders. The content script is different from the popup because it is not related to it. Indeed, it is not the child of any of the components previously mentioned. For this reason it is like to have a second App. The amazing part is that the only common thing they share is the store. This way you can get the access to the information and change the state of the app through the store. Again, in order to achieve this, we need our bridge which is the same shown before:

const store = new Store({  portName: 'COUNTING' })

As you can see, the port name is the same. In order to inject your code in the DOM of the page you need to render the App in an event listener and this is going to be:

window.addEventListener('load', () => { // HERE YOUR CODE})

A quick note about the debugging. You can get the access to the console by inspecting the page where you injected the script.

I hope that this article helped you to understand better how to integrate a Chrome Extension with React and Redux. If something is not clear feel free to write a comment.

--

--

Pier Roberto Lucisano
Codewords

Passionate Front End engineer seeking fresh challenges, coding the future.