├── src ├── vite-env.d.ts ├── background.ts ├── main.tsx ├── App.css ├── App.tsx ├── index.css └── assets │ └── react.svg ├── tsconfig.json ├── public ├── manifest.json └── vite.svg ├── .gitignore ├── tsconfig.node.json ├── index.html ├── .eslintrc.cjs ├── tsconfig.app.json ├── vite.config.ts ├── package.json └── README.md /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.app.json" 6 | }, 7 | { 8 | "path": "./tsconfig.node.json" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/background.ts: -------------------------------------------------------------------------------- 1 | chrome.runtime.onInstalled.addListener(() => { 2 | console.log('Chrome extension installed'); 3 | }); 4 | 5 | chrome.action.onClicked.addListener((tab) => { 6 | console.log('Extension icon clicked', tab); 7 | }); -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.tsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')!).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "React Chrome Extension", 4 | "version": "1.0.0", 5 | "description": "A simple React app as a Chrome extension", 6 | "action": { 7 | "default_popup": "index.html" 8 | }, 9 | "background": { 10 | "service_worker": "background.js" 11 | }, 12 | "permissions": [] 13 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | build 16 | 17 | # Editor directories and files 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 5 | "skipLibCheck": true, 6 | "module": "ESNext", 7 | "moduleResolution": "bundler", 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "noEmit": true 11 | }, 12 | "include": ["vite.config.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React Chrome Extension 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | "target": "ES2020", 6 | "useDefineForClassFields": true, 7 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 8 | "module": "ESNext", 9 | "skipLibCheck": true, 10 | 11 | /* Bundler mode */ 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "moduleDetection": "force", 17 | "noEmit": true, 18 | "jsx": "react-jsx", 19 | 20 | /* Linting */ 21 | "strict": true, 22 | "noUnusedLocals": true, 23 | "noUnusedParameters": true, 24 | "noFallthroughCasesInSwitch": true 25 | }, 26 | "include": ["src"] 27 | } 28 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | import { viteStaticCopy } from 'vite-plugin-static-copy'; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | react(), 8 | viteStaticCopy({ 9 | targets: [ 10 | { 11 | src: 'public/manifest.json', 12 | dest: '.', 13 | } 14 | ], 15 | }), 16 | ], 17 | build: { 18 | outDir: 'build', 19 | rollupOptions: { 20 | input: { 21 | main: './index.html', 22 | background: './src/background.ts', 23 | }, 24 | output: { 25 | entryFileNames: (chunkInfo) => { 26 | return chunkInfo.name === 'background' ? 'background.js' : 'assets/[name]-[hash].js'; 27 | }, 28 | }, 29 | }, 30 | }, 31 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chrome-extension-react-template", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc -b && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.1.1", 14 | "react-dom": "^19.1.1" 15 | }, 16 | "devDependencies": { 17 | "@types/chrome": "^0.1.4", 18 | "@types/react": "^19.1.12", 19 | "@types/react-dom": "^19.1.9", 20 | "@typescript-eslint/eslint-plugin": "^8.0.0", 21 | "@typescript-eslint/parser": "^8.0.0", 22 | "@vitejs/plugin-react": "^5.0.0", 23 | "eslint": "^9.0.0", 24 | "eslint-plugin-react-hooks": "^5.0.0", 25 | "eslint-plugin-react-refresh": "^0.4.14", 26 | "typescript": "^5.6.0", 27 | "vite": "^6.3.5", 28 | "vite-plugin-static-copy": "^3.1.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import reactLogo from './assets/react.svg' 3 | import viteLogo from '/vite.svg' 4 | import './App.css' 5 | 6 | function App() { 7 | const [count, setCount] = useState(0) 8 | 9 | return ( 10 | <> 11 |
12 | 13 | Vite logo 14 | 15 | 16 | React logo 17 | 18 |
19 |

Vite + React

20 |
21 | 24 |

25 | Edit src/App.tsx and save to test HMR 26 |

27 |
28 |

29 | Click on the Vite and React logos to learn more 30 |

31 | 32 | ) 33 | } 34 | 35 | export default App 36 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | 16 | a { 17 | font-weight: 500; 18 | color: #646cff; 19 | text-decoration: inherit; 20 | } 21 | a:hover { 22 | color: #535bf2; 23 | } 24 | 25 | body { 26 | margin: 0; 27 | display: flex; 28 | place-items: center; 29 | min-width: 320px; 30 | min-height: 100vh; 31 | } 32 | 33 | h1 { 34 | font-size: 3.2em; 35 | line-height: 1.1; 36 | } 37 | 38 | button { 39 | border-radius: 8px; 40 | border: 1px solid transparent; 41 | padding: 0.6em 1.2em; 42 | font-size: 1em; 43 | font-weight: 500; 44 | font-family: inherit; 45 | background-color: #1a1a1a; 46 | cursor: pointer; 47 | transition: border-color 0.25s; 48 | } 49 | button:hover { 50 | border-color: #646cff; 51 | } 52 | button:focus, 53 | button:focus-visible { 54 | outline: 4px auto -webkit-focus-ring-color; 55 | } 56 | 57 | @media (prefers-color-scheme: light) { 58 | :root { 59 | color: #213547; 60 | background-color: #ffffff; 61 | } 62 | a:hover { 63 | color: #747bff; 64 | } 65 | button { 66 | background-color: #f9f9f9; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Chrome Extension Template 2 | 3 | This is a template for creating a Chrome extension using React and [Vite](https://vitejs.dev/) with TypeScript. 4 | 5 | 6 | ## Getting Started 7 | 8 | ### Prerequisites 9 | 10 | Make sure you have [Node.js](https://nodejs.org/) (version 18+ or 20+) installed on your machine. 11 | 12 | ### Setup 13 | 14 | 1. Clone or fork the repository : 15 | 16 | ```sh 17 | # To clone 18 | git clone https://github.com/5tigerjelly/chrome-extension-react-template 19 | cd chrome-extension-react-template 20 | ``` 21 | 22 | 2. Install the dependencies: 23 | 24 | ```sh 25 | npm install 26 | ``` 27 | 28 | ## 🏗️ Development 29 | 30 | To start the development server: 31 | 32 | ```sh 33 | npm run dev 34 | ``` 35 | 36 | This will start the Vite development server and open your default browser. 37 | 38 | ## 📦 Build 39 | 40 | To create a production build: 41 | 42 | ```sh 43 | npm run build 44 | ``` 45 | 46 | This will generate the build files in the `build` directory. 47 | 48 | ## 📂 Load Extension in Chrome 49 | 50 | 1. Open Chrome and navigate to `chrome://extensions/`. 51 | 2. Enable "Developer mode" using the toggle switch in the top right corner. 52 | 3. Click "Load unpacked" and select the `build` directory. 53 | 54 | Your React app should now be loaded as a Chrome extension! 55 | 56 | ## 🗂️ Project Structure 57 | 58 | - `public/`: Contains static files and the `manifest.json`. 59 | - `src/`: Contains the React app source code. 60 | - `vite.config.ts`: Vite configuration file. 61 | - `tsconfig.json`: TypeScript configuration file. 62 | - `package.json`: Contains the project dependencies and scripts. 63 | 64 | ## License 65 | 66 | This project is licensed under the MIT License. -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------