├── src ├── vite-env.d.ts ├── chrome-extension │ ├── public │ │ ├── 16.png │ │ ├── 192.png │ │ ├── 32.png │ │ └── 48.png │ ├── popup │ │ └── index.tsx │ ├── global.css │ ├── options │ │ └── index.tsx │ └── manifest.json ├── main.tsx └── optionsLocal.tsx ├── tsconfig.node.tsbuildinfo ├── postcss.config.js ├── tsconfig.json ├── tsconfig.app.tsbuildinfo ├── tailwind.config.js ├── popup-local.css ├── options-local.css ├── options.html ├── popup.html ├── .gitignore ├── tsconfig.node.json ├── tsconfig.app.json ├── eslint.config.js ├── local.css ├── package.json ├── vite.config.ts ├── popup-local.html ├── options-local.html └── README.md /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.node.tsbuildinfo: -------------------------------------------------------------------------------- 1 | {"root":["./vite.config.ts"],"version":"5.6.2"} -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /src/chrome-extension/public/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omribarmats/chrome-extension-starter/HEAD/src/chrome-extension/public/16.png -------------------------------------------------------------------------------- /src/chrome-extension/public/192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omribarmats/chrome-extension-starter/HEAD/src/chrome-extension/public/192.png -------------------------------------------------------------------------------- /src/chrome-extension/public/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omribarmats/chrome-extension-starter/HEAD/src/chrome-extension/public/32.png -------------------------------------------------------------------------------- /src/chrome-extension/public/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omribarmats/chrome-extension-starter/HEAD/src/chrome-extension/public/48.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "./tsconfig.app.json" }, 5 | { "path": "./tsconfig.node.json" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.app.tsbuildinfo: -------------------------------------------------------------------------------- 1 | {"root":["./src/main.tsx","./src/optionslocal.tsx","./src/vite-env.d.ts","./src/chrome-extension/options/index.tsx","./src/chrome-extension/popup/index.tsx"],"version":"5.6.2"} -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["/popup-local.html", "./src/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /src/chrome-extension/popup/index.tsx: -------------------------------------------------------------------------------- 1 | import "../global.css"; 2 | 3 | export const Popup = () => { 4 | return ( 5 |
6 |
This is your popup.
7 |
8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /src/chrome-extension/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | * { 6 | margin: 0; 7 | padding: 0; 8 | box-sizing: border-box; 9 | } 10 | 11 | html { 12 | min-height: 100%; 13 | position: relative; 14 | } 15 | -------------------------------------------------------------------------------- /src/chrome-extension/options/index.tsx: -------------------------------------------------------------------------------- 1 | import "../global.css"; 2 | 3 | const Options = () => { 4 | return ( 5 |
6 |
This is your options page.
7 |
8 | ); 9 | }; 10 | 11 | export default Options; 12 | -------------------------------------------------------------------------------- /popup-local.css: -------------------------------------------------------------------------------- 1 | .content { 2 | max-width: 1200px; 3 | display: flex; 4 | margin: auto; 5 | padding: 10px; 6 | flex-direction: row; 7 | justify-content: space-between; 8 | } 9 | 10 | .content-text { 11 | color: white; 12 | font-size: large; 13 | font-weight: 500; 14 | padding: 30px 50px 0 0; 15 | } 16 | -------------------------------------------------------------------------------- /options-local.css: -------------------------------------------------------------------------------- 1 | .content { 2 | display: flex; 3 | margin: auto; 4 | flex-direction: column; 5 | gap: 30px; 6 | padding: 30px; 7 | justify-content: space-between; 8 | } 9 | 10 | .content-text { 11 | max-width: 1200px; 12 | margin: auto; 13 | color: white; 14 | font-size: large; 15 | font-weight: 500; 16 | } 17 | -------------------------------------------------------------------------------- /options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Options 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Popup 7 | 8 | 9 |
10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /.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 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import { Popup } from "./chrome-extension/popup/index"; 4 | import "./chrome-extension/global.css"; 5 | 6 | createRoot(document.getElementById("root")!).render( 7 | 8 |
9 | 10 |
11 |
12 | ); 13 | -------------------------------------------------------------------------------- /src/optionsLocal.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import Options from "./chrome-extension/options/index"; 4 | import "./chrome-extension/global.css"; 5 | 6 | createRoot(document.getElementById("root")!).render( 7 | 8 |
9 | 10 |
11 |
12 | ); 13 | -------------------------------------------------------------------------------- /src/chrome-extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "Chrome Extension Starter", 4 | "version": "1.0.0", 5 | "action" : { 6 | "default_popup": "popup.html" 7 | }, 8 | "options_page": "options.html", 9 | "description": "This is the description of your extension", 10 | "icons": { 11 | "16": "public/16.png", 12 | "32": "public/32.png", 13 | "48": "public/48.png", 14 | "192": "public/192.png" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "lib": ["ES2023"], 5 | "module": "ESNext", 6 | "skipLibCheck": true, 7 | 8 | /* Bundler mode */ 9 | "moduleResolution": "bundler", 10 | "allowImportingTsExtensions": true, 11 | "isolatedModules": true, 12 | "moduleDetection": "force", 13 | "noEmit": true, 14 | 15 | /* Linting */ 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true 20 | }, 21 | "include": ["vite.config.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "isolatedModules": true, 13 | "moduleDetection": "force", 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src", "src/main.tsx"] 24 | } 25 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import reactHooks from 'eslint-plugin-react-hooks' 4 | import reactRefresh from 'eslint-plugin-react-refresh' 5 | import tseslint from 'typescript-eslint' 6 | 7 | export default tseslint.config( 8 | { ignores: ['dist'] }, 9 | { 10 | extends: [js.configs.recommended, ...tseslint.configs.recommended], 11 | files: ['**/*.{ts,tsx}'], 12 | languageOptions: { 13 | ecmaVersion: 2020, 14 | globals: globals.browser, 15 | }, 16 | plugins: { 17 | 'react-hooks': reactHooks, 18 | 'react-refresh': reactRefresh, 19 | }, 20 | rules: { 21 | ...reactHooks.configs.recommended.rules, 22 | 'react-refresh/only-export-components': [ 23 | 'warn', 24 | { allowConstantExport: true }, 25 | ], 26 | }, 27 | }, 28 | ) 29 | -------------------------------------------------------------------------------- /local.css: -------------------------------------------------------------------------------- 1 | body { 2 | min-height: 100%; 3 | width: 100vw; 4 | background: linear-gradient(to bottom, #807fba, #34005d); 5 | } 6 | 7 | .navbar { 8 | background-color: #807fba/80; 9 | color: white; 10 | font-size: x-large; 11 | border-bottom: 1px gainsboro solid; 12 | margin-bottom: 20px; 13 | } 14 | 15 | .inner-navbar { 16 | max-width: 1200px; 17 | width: 100%; 18 | display: flex; 19 | margin: auto; 20 | padding: 10px; 21 | flex-direction: row; 22 | justify-content: space-between; 23 | } 24 | 25 | .title { 26 | font-weight: 700; 27 | font-size: x-large; 28 | margin-top: auto; 29 | margin-bottom: auto; 30 | } 31 | 32 | .menu { 33 | display: flex; 34 | gap: 10px; 35 | } 36 | 37 | .menu > * { 38 | border: 2px solid white; 39 | font-size: x-large; 40 | border-radius: 7px; 41 | padding: 5px 10px; 42 | font-weight: 700; 43 | transition: all 0.3s ease; 44 | } 45 | 46 | .menu > *:hover { 47 | border: 2px solid #34005d; 48 | background-color: white; 49 | color: #34005d; 50 | } 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chrome-extension-starter", 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 .", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@types/node": "^22.5.5", 14 | "react": "^18.3.1", 15 | "react-dom": "^18.3.1", 16 | "vite-plugin-static-copy": "^1.0.6" 17 | }, 18 | "devDependencies": { 19 | "@eslint/js": "^9.9.0", 20 | "@types/chrome": "^0.0.271", 21 | "@types/react": "^18.3.3", 22 | "@types/react-dom": "^18.3.0", 23 | "@vitejs/plugin-react": "^4.3.1", 24 | "autoprefixer": "^10.4.20", 25 | "eslint": "^9.9.0", 26 | "eslint-plugin-react-hooks": "^5.1.0-rc.0", 27 | "eslint-plugin-react-refresh": "^0.4.9", 28 | "globals": "^15.9.0", 29 | "postcss": "^8.4.47", 30 | "tailwindcss": "^3.4.11", 31 | "typescript": "^5.5.3", 32 | "typescript-eslint": "^8.0.1", 33 | "vite": "^5.4.5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import { viteStaticCopy } from "vite-plugin-static-copy"; 4 | import { resolve } from "path"; 5 | 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [ 9 | react(), 10 | viteStaticCopy({ 11 | targets: [ 12 | { src: "src/chrome-extension/manifest.json", dest: "." }, 13 | { src: "src/chrome-extension/public/16.png", dest: "./public" }, 14 | { src: "src/chrome-extension/public/32.png", dest: "./public" }, 15 | { src: "src/chrome-extension/public/48.png", dest: "./public" }, 16 | { src: "src/chrome-extension/public/192.png", dest: "./public" }, 17 | ], 18 | }), 19 | ], 20 | server: { 21 | open: "/popup-local.html", 22 | }, 23 | build: { 24 | rollupOptions: { 25 | input: { 26 | popup: resolve(__dirname, "popup.html"), 27 | options: resolve(__dirname, "options.html"), 28 | }, 29 | output: { 30 | entryFileNames: "[name].js", 31 | }, 32 | }, 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /popup-local.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Popup 8 | 9 | 10 | 11 | 12 | 13 | 22 |
23 |
24 | This is your chrome extension popup preview. To make changes go to src/chrome-extension/popup/index.tsx 25 |
26 |
27 | 28 |
29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /options-local.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | App 7 | 8 | 9 | 10 | 11 | 12 | 13 | 22 |
23 |
24 | This is your chrome extension options page preview. To make changes go to src/chrome-extension/options/index.tsx 25 |
26 |
27 | 28 |
29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chrome Extension Starter with Vite, React, TypeScript, and Tailwind CSS 2 | This project is a starter template for building modern Chrome extensions using Vite, React, TypeScript, and Tailwind CSS. It simplifies the setup so you can focus on building your extension's features. 3 | 4 |
5 | image 1 6 | image 2 7 | image 3 8 | image 4 9 |
10 | 11 | ## View tutorial on YouTube 12 | 13 | YouTube video 14 | 15 | 16 | ## Features 17 | - **Fast reloading** develop UI faster, view the popup and options page 18 | - **Vite** for fast bundling and development 19 | - **React** for building interactive UI components 20 | - **TypeScript** for type-safe JavaScript development 21 | - **Tailwind CSS** for easy and responsive styling 22 | - **chrome-types** Chrome's API TS files for auto-completion 23 | 24 | ## Installation 25 | 26 | ### Clone this repository: 27 | ``` 28 | git clone https://github.com/omribarmats/chrome-extension-starter.git new-project 29 | ``` 30 | * Replace `new-project` with your project name 31 | 32 | ### Open the new directory: 33 | ``` 34 | cd new-project 35 | ``` 36 | ### Install dependencies: 37 | ``` 38 | npm install 39 | ``` 40 | ### Start the development server: 41 | ``` 42 | npm run dev 43 | ``` 44 | ## Load the Extension 45 | 46 | 1. Run the build command: `npm run build.` 47 | 2. Go to `chrome://extensions/` in your Chrome browser. 48 | 3. Enable `Developer mode`. 49 | 4. Click `Load unpacked` and select the `dist` folder from the project. 50 | 51 | ## Development 52 | - Hot-reload enabled for easier development. 53 | - Modify your code in the src folder. 54 | - Tailwind CSS is already configured and ready to use. 55 | - Run `npm run build` to implement changes to `dist` folder 56 | - Go on `chrome://extensions/` and click refresh `⟳` 57 | 58 | ### How to change the popup? 59 | - Go on `src/chrome-extension/popup/index.tsx` 60 | - Once changes are made open the terminal and run `npm run build` then visit `chrome://extensions/` and click the refresh `⟳` button on your extension 61 | 62 | ### How to change the options page? 63 | - Go on `src/chrome-extension/options/index.tsx` 64 | - Once changes are made open the terminal and run `npm run build` then visit `chrome://extensions/` and click the refresh `⟳` button on your extension 65 | 66 | - ### How to add a background script? 67 | - Create a `background.ts` file inside the `src` folder 68 | - Go on `vite.config.ts` and add this line `background: resolve(__dirname, "src/background.ts"),` under `build.rollupOptions.input` 69 | - For example 70 | ``` 71 | build: { 72 | rollupOptions: { 73 | input: { 74 | popup: resolve(__dirname, "popup.html"), 75 | options: resolve(__dirname, "options.html"), 76 | background: resolve(__dirname, "src/background.ts"), 77 | }, 78 | output: { 79 | entryFileNames: "[name].js", 80 | }, 81 | }, 82 | }, 83 | ``` 84 | - Go on `manifest.json` and add this code: 85 | ``` 86 | "background": { 87 | "service_worker": "background.js", 88 | "type": "module" 89 | } 90 | ``` 91 | - Open the terminal and run `npm run build` then visit `chrome://extensions/` and click the refresh `⟳` button on your extension 92 | 93 | ## Contributing 94 | Feel free to fork the project and make improvements or submit bug reports or issues. 95 | --------------------------------------------------------------------------------