A starter repo for building browser extensions with React and Typescript.
Here's what you get:
- ⚛️ content scripts, popup and options page as three separate React (v18.2+) apps
- 𝙏𝙎 Typescript (v5.0+) on all apps and other scripts
- 💬 a common communication channel and storage for all these apps and scripts
- 🧩 works with Manifest V3 (i.e. works with all Chromium-based browsers and soon Firefox)
Short version:
# install dependencies in all apps thanks to Yarn workspaces
yarn
# run
cd content_scripts/app && yarn start
cd options && yarn start
cd popup && yarn start
# to build
yarn build
# to package into an archive
yarn zipMore details:
- learn more about WebExtensions and Manifest files (see resources section)
- update information inside
manifest.json(name, description, homepage,...) and update icons - code and test your React apps within
/content_scripts/app,/optionsand/popup- all three folders are regular React apps bundled via Vite, so just refer to their
package.jsonandvite.config.jsfor config, and write code like you would for any other React app - if you don't need some of these apps or scripts, simply remove them from the project and remove their build steps in the build pipeline (inside
./build.sh)
- all three folders are regular React apps bundled via Vite, so just refer to their
- build/compile your extension by running
yarn buildfrom the root folder (this executes./build.sh), load it in the browser and try it there- you can use generated
./buildfolder to load the "unpacked" version of the extension in the browser and test it locally (see how for Chrome or for Firefox)
- you can use generated
- pack the extension into a single archive
- run
yarn zipfrom the root folder
- run
- publish it on the web stores
- instructions for publishing to Chrome Web Store
- instructions for publishing to Firefox Add-ons
Docs and guides:
- Chrome API reference
- MV3 architecture overview (Chrome)
- Manifest V3 file format
- Anatomy of a Web Extension (Firefox)
- Building a cross-browser extension
- Porting a Google Chrome extension
Useful code repos:
Web extensions, and thus this repo, consist of four large parts, plus the manifest file specifying each of those and some additional meta-information. The parts are: content scripts, action/popup, options page and background scripts. A single web extension can have any combination of these, neither of all of them (i.e. all are optional); only the manifest is mandatory.
- The root folder (
./) holds basic meta-information about the extension, i.e. its manifest and icons. /content_scriptsis a place for the extra JS and CSS that gets injected into the pages that you view while your extension is installed and enabled./appfolder within it holds the React app where you basically write the entirety of logic, and the remaining files are there to create a container for your React root (similar to howdiv#rootexists inpublic/index.htmlwhen using a regular CRA)./popupis a place for another React app that gets displayed as a popup/dropdown when clicking on the extension's icon displayed near the address bar in the browser./optionshosts a React app that serves an Options page for the extension where you can configure and persists whatever some common options./workeris a place for a service worker (MV3) or a background script (MV2). These scripts can never have any UI as a part of it, so it consists only of TS/JS files, and they run in the background.
By having a very predictable build output (a single JS bundle) for each part of our extension, we can hardcode their script files inside manifest.json and that's it - always the same output, always the same entry points and script files, i.e. no need for programmatic injection, dynamic loading or anything complex.
This is a build output:
You can see how that exactly matches the files specified inside of manifest.json:

