├── latestBuild.zip ├── public ├── images │ ├── react-logo-128.png │ ├── react-logo-16.png │ ├── react-logo-32.png │ └── react-logo-48.png ├── background.js ├── index.html └── manifest.json ├── src ├── index.js ├── index.css ├── App.js ├── App.css └── logo.svg ├── .gitignore ├── Makefile ├── package.json └── README.md /latestBuild.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casendler/react-chrome-extension-mv3/HEAD/latestBuild.zip -------------------------------------------------------------------------------- /public/images/react-logo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casendler/react-chrome-extension-mv3/HEAD/public/images/react-logo-128.png -------------------------------------------------------------------------------- /public/images/react-logo-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casendler/react-chrome-extension-mv3/HEAD/public/images/react-logo-16.png -------------------------------------------------------------------------------- /public/images/react-logo-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casendler/react-chrome-extension-mv3/HEAD/public/images/react-logo-32.png -------------------------------------------------------------------------------- /public/images/react-logo-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casendler/react-chrome-extension-mv3/HEAD/public/images/react-logo-48.png -------------------------------------------------------------------------------- /public/background.js: -------------------------------------------------------------------------------- 1 | /*global chrome*/ 2 | chrome.runtime.onInstalled.addListener(() => { 3 | console.log('Chrome extension successfully installed!'); 4 | return; 5 | }); 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 380px; 3 | height: 400px; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 6 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 7 | sans-serif; 8 | -webkit-font-smoothing: antialiased; 9 | -moz-osx-font-smoothing: grayscale; 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | /dist 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React Chrome Extension 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | /*global chrome*/ 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | function App() { 6 | return ( 7 |
8 |
9 | logo 10 |

Woah it's a React Chrome extension...

11 | 17 | View Extension Dev Docs 18 | 19 |
20 |
21 | ); 22 | } 23 | 24 | export default App; 25 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #################################### 2 | # Build command for Chrome Extension 3 | #################################### 4 | 5 | .PHONY: help build 6 | 7 | help: 8 | $(info ${HELP_MESSAGE}) 9 | @exit 0 10 | 11 | build: 12 | @echo 'Removing prior build...' 13 | @rm -rf dist/* 14 | @rm -f latestBuild.zip 15 | @echo 'Prior build removed!' 16 | @echo 'Preparing new extension build..' 17 | @export INLINE_RUNTIME_CHUNK=false; \ 18 | export GENERATE_SOURCEMAP=false; \ 19 | yarn build 20 | @mkdir -p dist 21 | @cp -r build/* dist 22 | @echo 'Renaming files...' 23 | @mv dist/index.html dist/popup.html 24 | @echo 'Zipping up build files for upload...' 25 | @zip -r -X latestBuild.zip dist/* 26 | @echo 'New extension build ready for upload!' 27 | @exit 0 28 | 29 | define HELP_MESSAGE 30 | 31 | --- Run this command to prepare the build for upload --- 32 | $ make build 33 | 34 | endef 35 | 36 | 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-chrome-extension-mv3", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-scripts": "4.0.3" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": [ 21 | "react-app", 22 | "react-app/jest" 23 | ] 24 | }, 25 | "browserslist": { 26 | "production": [ 27 | ">0.2%", 28 | "not dead", 29 | "not op_mini all" 30 | ], 31 | "development": [ 32 | "last 1 chrome version", 33 | "last 1 firefox version", 34 | "last 1 safari version" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "React Chrome Extension", 4 | "version": "0.1", 5 | "description": "Boilerplate manifest for React based Chrome Extension using Google's Manifest v3", 6 | "background": { 7 | "service_worker": "./background.js" 8 | }, 9 | "permissions": [], 10 | "author": "Christian Sendler", 11 | "homepage_url": "https://github.com/casendler/", 12 | 13 | "short_name": "React Extension", 14 | "action": { 15 | "default_popup": "popup.html", 16 | "default_title": "React Chrome Extension", 17 | "default_icon": { 18 | "16": "/images/react-logo-16.png", 19 | "32": "/images/react-logo-32.png", 20 | "48": "/images/react-logo-48.png", 21 | "128": "/images/react-logo-128.png" 22 | } 23 | }, 24 | "icons": { 25 | "16": "/images/react-logo-16.png", 26 | "32": "/images/react-logo-32.png", 27 | "48": "/images/react-logo-48.png", 28 | "128": "/images/react-logo-128.png" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Chrome Extension (Manifest v3) Boilerplate 2 | 3 | Starting point for creating a Chrome Browser Extension popup and service-worker based background script. Contains a custom Makefile to prepare your build for an unpacked local upload to chrome://extensions and prepares a zip file for direct upload to the Chrome Web Store. 4 | 5 | Traditionally browser extensions are built using vanilla JS. While this is certainly feasible, it is often cumbersome and inefficient when developing more complex extensions with a rich UI. 6 | 7 | ## Available Scripts 8 | 9 | In the root project directory, you can run: 10 | 11 | ### `make build` 12 | 13 | Prepares the app build for upload to chrome://extensions/ and the Google Chrome Webstore \ 14 | It correctly bundles your project and optimizes the build for use as a Chrome Extension. \ 15 | 16 | This command creates two things: 17 | 18 | 1. A `./dist` directory that contains the files for `chrome://extensions/` -- use this folder to load the unpacked extension 19 | 2. A file called latestBuild.zip, this .zip can be uploaded to the Developer Console when submitting your extension to the Chrome Web Store 20 | 21 | #### Known Limitations 22 | 23 | - Does not currently handle [Options](https://developer.chrome.com/docs/extensions/mv3/options/) or [Content Scripts](https://developer.chrome.com/docs/extensions/mv3/content_scripts/). For most use cases the options.html page from Chrome is unecessary as you can build an options menu into your popup directly. In order to enable content scripts and the options page, I recommend using `react-app-rewired` to replace the standard single entry point with three entry points for the popup, options, and content scripts. 24 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------