├── 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 |
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 |
14 |
15 | Chrome Extension Starter
16 |
20 |
21 |
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 |
14 |
15 | Chrome Extension Starter
16 |
20 |
21 |
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 |
10 |
11 | ## View tutorial on YouTube
12 |
13 |
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 |
--------------------------------------------------------------------------------