Creating your own Chrome Extensions with React is easier than you think.

Chrome extensions are a great way to improve a user’s workflow. They reduce context switching and enable the developer’s flow. Here’s how to make them in React.

Creating a React application
Making it an extension
Adding Functionality
Adding it to Google Chrome Webstore

Creating a React application

The first thing you’ll want to do is create your React application. To streamline this process we’ll use create-react-app in combination with the Typescript template. In this guide, we’ll create a simple extension for rolling dice. Feel free to create your own, just pay attention to the file names, variable names, and other named parameters.

npx create-react-app roll-dice-extension --template typescript
Chrome Extension with React

Making it an extension

Once your app is created, you’ll see a familiar folder structure. Navigate to your public folder and update config.yaml. This is a file that specifies the app name, description, icons, and other parameters used by Chrome.

{
"short_name": "Roll Dice",
"name": "A simple extension for rolling dice",
"icons": {
"16": "/logo16.png",
"32": "/logo32.png",
"48": "/logo48.png",
"128": "/logo128.png"
},
"version": "1.0",
"manifest_version": 3,
"action": {
"default_popup": "index.html"
}
}

Before building and uploading your extension to Chrome you’ll have to update the package.json build script. Replace it with the following.

"build": "set \"INLINE_RUNTIME_CHUNK=false\" && react-scripts build"

This simply removes the unsafe, inline Javascript code from your React application which is not allowed in Chrome Extensions. Now the extension can finally be built by running npm run build and going to Chrome where you’ll entering chrome://extensions in the URL bar. This leads you to the extensions page, where the extension can be uploaded. To do this you must first enable developer mode with the toggle in your top right corner and then click on the button that says “Load unpacked” and load your extension by selecting the build folder in your project.

That’s it. Your extension should now be available for you to run. Go to your extensions, pin it to the Chrome header and try clicking. The following image should appear.

Your basic React app running as an extension

If you want to check out an extension in production, feel free to check out Math Embed — my extension for simple embedding of math equations in Medium. Otherwise, follow along to see how to add basic functionality or go ahead and create your own. (Github for Math Embed)

Adding Functionality

Adding functionality from now on is all a matter of upgrading your React application. We won’t be focusing too much on the details of how it works, since I’m assuming you already have a decent understanding of how React works.

The extension we’ll be making

Run npm run start to start up your development server. You should see the same React app as you saw in the extension, just larger. We need none of that, so simply delete all tsx code in App.tsx.

//App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
return (

);
}
export default App;

To make our lives a bit easier we’ll install Material UI npm install @material-ui/core and create a basic theme and layout in our App.tsx file.

//App.tsx
import {
Box,
Button,
createMuiTheme,
ThemeProvider,
Typography,
} from "@material-ui/core";
import { useState } from "react";
import "./App.css";
import RollDiceButton from "./RollDiceButton";
import RollDisplay from "./RollDisplay";
const theme = createMuiTheme({
typography: {
allVariants: {
color: "#FFFFFF",
},
fontFamily: [
"Poppins",
"sans-serif",
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
].join(","),
fontSize: 14,
h1: {
fontSize: 24,
fontWeight: 700,
},
},
});
function App() {return (
<ThemeProvider theme={theme}>
<Box
height="100%"
flex={1}
display="flex"
justifyContent="center"
alignItems="center"
flexDirection="column"
bgcolor="#023047"
>
</Box>
</ThemeProvider>
);
}
export default App;

With this out of the way, we can take a look at the rest of the functionalities of our app. We’ll be developing:

  • A basic display of all throws we made
  • A sum of all the throws
  • Buttons to increase and decrease the number of dice thrown
  • And a button to throw dice.

To achieve these functionalities we’ll develop the following:

  • States holding the dice count and separate rolls
  • RollDiceButton: A button that takes dice count as the input and returns separate rolls
  • RollDisplay: A display of separate rolls and their sum
  • Buttons to increase or decrease dice count

We begin by defining two states in App.tsx— one that stores the separate dice throw values and one that stores the current number of dice we’re throwing.

const [diceRolls, setDiceRolls] = useState<number[]>([]);
const [count, setCount] = useState(2);

Next, we’ll create a new file for the button for rolling dice RollDiceButton.tsx. The button will take count as the dice count and setDiceRolls that will set the state in the parent component.

We will also add simple functions to generate dice rolls getDiceRolls() and getDiceRolls(count: number), which will get triggered on button click.

import { Box, Button } from "@material-ui/core";
import React, { useEffect } from "react";
interface RollDiceButtonProps {
count: number;
setDiceRolls: React.Dispatch<React.SetStateAction<number[]>>;
}
const getDiceRoll = () => {
return Math.ceil(Math.random() * 6);
};
const getDiceRolls = (count: number) => {
var rolls: number[] = [];
for (var i = 0; i < count; i++) {
rolls.push(getDiceRoll());
}
return rolls;
};
const RollDiceButton: React.FC<RollDiceButtonProps> = (props) => {
useEffect(() => {
props.setDiceRolls(getDiceRolls(props.count));
}, [props.count]);
return (
<Box>
<Button
variant="contained"
color="primary"
onClick={() => props.setDiceRolls(getDiceRolls(props.count))}
>
Roll Dice
</Button>
</Box>
);
};
export default RollDiceButton;

Secondly, we’ll create the display component RollDisplay.tsx. It will take the separate dice diceRolls rolls as input and display them in a structured manner including a sum of the rolls.

import { Box, Typography } from "@material-ui/core";
import React from "react";
interface RollDisplayProps {
diceRolls: number[];
}
const RollDisplay: React.FC<RollDisplayProps> = (props) => {
return (
<Box
display="flex"
justifyContent="center"
alignItems="center"
flexDirection="column"
>
<Box display="flex">
{props.diceRolls.map((roll) => (
<Box padding={1}>
<Typography>{roll}</Typography>
</Box>
))}
</Box>
<Box padding={1}>
<Typography variant="h1">
{props.diceRolls.reduce((acc, value) => acc + value)}
</Typography>
</Box>
</Box>
);
};
export default RollDisplay;

Finally, we add them to App.tsx. We import the required components and include them within the ThemeProvider.

import {
Box,
Button,
createMuiTheme,
ThemeProvider,
Typography,
} from "@material-ui/core";
import { useState } from "react";
import "./App.css";
import RollDiceButton from "./RollDiceButton";
import RollDisplay from "./RollDisplay";
const theme = createMuiTheme({
typography: {
allVariants: {
color: "#FFFFFF",
},
fontFamily: [
"Poppins",
"sans-serif",
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
].join(","),
fontSize: 14,
h1: {
fontSize: 24,
fontWeight: 700,
},
},
});
function App() {
const [diceRolls, setDiceRolls] = useState<number[]>([0, 0]);
const [count, setCount] = useState(2);
return (
<ThemeProvider theme={theme}>
<Box
height="100%"
flex={1}
display="flex"
justifyContent="center"
alignItems="center"
flexDirection="column"
bgcolor="#006d77"
padding="16px"
>
<Box
display="flex"
justifyContent="center"
alignItems="center"
flexDirection="row"
>
<Button onClick={() => setCount(count > 1 ? count - 1 : 1)}>
<Typography>-</Typography>
</Button>
<RollDisplay diceRolls={diceRolls}></RollDisplay>{" "}
<Button onClick={() => setCount(count + 1)}>
<Typography>+</Typography>
</Button>
</Box>
<RollDiceButton
setDiceRolls={setDiceRolls}
count={count}
></RollDiceButton>
</Box>

</ThemeProvider>
);
}
export default App;

That’s it for the simple extension. If you got lost anywhere, feel free to check out the Github repository for this project. Update it in chrome://extensions and check it out.

Adding the extension to Google Chrome Webstore

Before publishing, you may want to double check that the extension is working properly. If everything is to order, you are ready to publish it through the Chrome Webstore Developer Console.

Register or Log In the console click on +New Item in the top right corner, which will prompt you for a .zip file. Simply zip up your project’s buildfolder and drop it inside your browser.

Click on the +New Item to add a new extension to Google Chrome
Drop your .zip files here.

Some of the information will be pre-filled from your manifest.json file. The rest is up to you. After everything is filled out the “Submit for review” button will light up and you’ll be able to submit your extension for review. If it passes, you’ll see it added to the Chrome Web Store.

Submit for review button grayed out, because some info is filled incorrectly.

Congratulations. You made it to the end and your extension should be on the Web Store soon.

Make sure to give me a couple of claps if you found this guide useful and feel free to share it with anyone who might need it.

UI&UX Designer | Frontend developer | Masters Student

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store