Chrome Extension Dev
ASecurityUse to build, scaffold, or debug a Chrome/Edge/Brave extension — Manifest V3, a WXT project, a content script, a background service worker, popup/options UI, chrome.storage, message passing, or in-page floating UI. Trigger phrases: "build a chrome extension", "manifest v3", "content script", "WXT", "background service worker", "chrome.storage", "extension popup", "inject UI into a page". Covers project setup (WXT + React + TS, or plain Vite/CRXJS), MV3 entrypoints, typed messages with sender ...
- 2 stars
- 0 votes
- 0 copies
- 1 view
- Added September 24, 2026
Works with
Security analysis
100/100Pro scans all 9 files and shows the line behind each finding
npx -y skills add thomaslimited/chrome-extension-skills --skill chrome-extension-dev --agent claude-codeAre you the author of Chrome Extension Dev?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/thomaslimited-chrome-extension-dev)---
name: chrome-extension-dev
description: Use to build, scaffold, or debug a Chrome/Edge/Brave extension — Manifest V3, a WXT project, a content script, a background service worker, popup/options UI, chrome.storage, message passing, or in-page floating UI. Trigger phrases: "build a chrome extension", "manifest v3", "content script", "WXT", "background service worker", "chrome.storage", "extension popup", "inject UI into a page". Covers project setup (WXT + React + TS, or plain Vite/CRXJS), MV3 entrypoints, typed messages with sender validation, storage.local vs sync with quotas, alarms, a stable dev extension ID via manifest key, closed-Shadow-DOM UI with viewport-aware positioning, safe text insertion into textarea/contenteditable/rich editors, optional-permission gating, Vitest + Playwright e2e, build/zip packaging. Distilled from a shipped extension. Does NOT cover OAuth/auth, payments/billing, store listing copy, or CI/CD deploy — see chrome-extension-auth, chrome-extension-payments, chrome-web-store-listing, chrome-extension-deploy.
license: MIT
---
# Chrome Extension Dev (Manifest V3)
## Scope
This skill handles the **extension engineering** problem: MV3 project setup, entrypoints, message
passing, storage, content-script UI, permissions, testing, and packaging.
It does **not** handle:
- OAuth / sign-in flows, session tokens → `chrome-extension-auth`
- Payments, billing, entitlements → `chrome-extension-payments`
- Store listing copy, screenshots, privacy policy → `chrome-web-store-listing`
- CI/CD, release automation, store upload → `chrome-extension-deploy`
If the task touches those, do the extension-engineering part here and hand off the rest.
## Workflow
1. **Pick the stack.** Default to WXT + TypeScript unless the decision table in
`references/project-setup.md` says otherwise. Scaffold with the layout and `wxt.config.ts`
pattern in that file, including the dev `key` for a stable extension ID
(`assets/wxt.config.template.ts`).
2. **Design entrypoints before writing code.** Decide what runs in the background service worker
vs content script vs popup/options vs offscreen document. Read
`references/mv3-architecture.md` for the responsibility split and service-worker lifetime
rules.
3. **Define the message contract first.** One discriminated union per direction, validated at the
`runtime.onMessage` boundary. Read `references/messaging-and-storage.md` before writing a
single `sendMessage` call.
4. **Choose storage areas deliberately.** `local` is the source of truth; `sync` is opt-in,
quota-limited, and needs conflict resolution. Same reference file covers quotas and the
dirty-queue + alarm pattern.
5. **Build content-script UI in a closed Shadow DOM.** Never inject into the page's own DOM tree.
Follow `references/content-script-ui.md` for host creation, positioning (flip/clamp to
viewport), `isTrusted` guards, sensitive-field exclusion, and the "insert exactly once, verify,
fall back to clipboard" pattern for textarea/contenteditable/ProseMirror-like editors.
6. **Minimize permissions.** Request the smallest permission set in the base manifest; gate
anything sensitive (e.g. `clipboardRead`) behind `optional_permissions` and a user gesture from
an extension page. See the table below and `references/mv3-architecture.md`.
7. **Wire env/config at build time.** `VITE_*` variables with safe local defaults; never let a
production build silently point at localhost. See `references/project-setup.md`.
8. **Write tests as you go.** Vitest for pure logic (trigger matching, guards, storage merge
logic) with `happy-dom`; Playwright for the real unpacked extension (persistent context,
service worker, fixture pages). Read `references/testing.md` before wiring either.
9. **Build and package deliberately.** `wxt build` for unpacked/dev, `wxt zip --mode store` for
the Chrome Web Store upload (this must **not** carry the dev `key`). Naming and versioning in
`references/project-setup.md`.
10. **Before calling it done, check `references/pitfalls.md`** — real bugs from a shipped
extension (broken OAuth from a random store ID, a menu invisible at the bottom of the screen,
keyup resetting a keyboard highlight, a zip build overwriting the dev build, e2e unable to
query tabs). Each entry names the symptom, the cause, and the fix.
## Decision tables
### Stack
| Situation | Choose |
|---|---|
| New MV3 extension, needs a UI framework, wants a fast dev loop with HMR | **WXT + React + TypeScript** (this skill's default) |
| Team already has a Vite setup they want to keep, or needs a browser API WXT doesn't wrap yet | Plain **Vite + `@crxjs/vite-plugin`** |
| Extension is a single content script with no UI and no build step justified | Hand-written `manifest.json` + esbuild/tsc, no framework |
| Needs Manifest V2 | Out of scope — MV2 is being phased out; do not build new MV2 extensions |
### Storage area
| Need | Use |
|---|---|
| Source of truth, large data, no cross-device sync required | `chrome.storage.local` (request `unlimitedStorage` if it can grow) |
| Small, cross-device data (settings, small records) | `chrome.storage.sync`, mirrored from `local`, quota-checked before every write |
| One-shot handoff between background and a not-yet-open popup | `chrome.storage.session` |
| Anything bigger than the sync per-item quota | Keep it `local`-only; do not force it into `sync` |
### Permission
| Need | Use |
|---|---|
| Needed for the extension to function at all, on install | Base `permissions` |
| Sensitive, or only needed for a paid tier / opt-in feature | `optional_permissions`, requested via `chrome.permissions.request` from an extension page on a user gesture (never from a content script) |
| Only need the current tab when the user invokes the extension | `activeTab`, not a broad host permission |
| Need to enumerate/query tabs by URL | Add the `tabs` permission explicitly — `activeTab` alone will not expose tab URLs to `tabs.query` |
## Reference files
- `references/project-setup.md` — monorepo layout, `wxt.config.ts`, dev key, env vars, build/zip/versioning
- `references/mv3-architecture.md` — entrypoints, service worker lifetime, alarms, offscreen documents, permissions
- `references/content-script-ui.md` — closed Shadow DOM, positioning, guards, text insertion strategies
- `references/messaging-and-storage.md` — typed messages, sender validation, storage quotas and sync merge
- `references/testing.md` — Vitest unit tests, Playwright e2e against an unpacked build
- `references/pitfalls.md` — real bugs and fixes, checked before shipping
## Security
- Never commit `.env` files, OAuth client secrets, API keys, or the dev signing `key` pair to a
public repo; keep the dev key file gitignored and documented as regenerable.
- Never log tokens, session cookies, or full API responses containing user data from the
background script or content script — both can end up in bug reports.
- Validate every `runtime.onMessage` sender before acting on privileged messages (see
`references/messaging-and-storage.md`); a content script runs in the page's renderer and must be
treated as less trusted than the extension's own pages.
- Treat all content-script DOM events as page-controlled unless `event.isTrusted === true`.
Files in this skill
- SKILL.md
- assets/playwright-extension-fixtures.template.ts
- assets/wxt.config.template.ts
- references/content-script-ui.md
- references/messaging-and-storage.md
- references/mv3-architecture.md
- references/pitfalls.md
- references/project-setup.md
- references/testing.md
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.