CRXForge (Extension Template) โ a project by Ahsan Mahmood
Cross-browser (Chrome/Firefox/Edge) Manifest V3 extension starter on WXT, React 19, and TypeScript, store-compliant by default.
A rejection for remote code arrives after the work is finished.
That is the shape of the problem. The extension is built, the listing is written, the screenshots are cropped, and then a reviewer finds a script pointing at a content delivery network and the submission goes back. The repair is rarely small either. The offending dependency is usually the authentication SDK or the analytics tag, and both were chosen in week one.
CRXForge is a starter with those decisions already made.
Google sign-in runs through the Chrome Identity API rather than the Firebase Auth SDK. Analytics is bundled: Amplitude over HTTP, Firestore over REST, Sentry compiled in, nothing fetched at runtime. The content security policy is a strict script-src of self. Permissions start minimal and grow only when something needs them.
None of that is clever.
All of it is the difference between a submission and a resubmission, which is measured in weeks rather than in hours when a store queue is involved.
One codebase builds for Chrome, Firefox and Edge through WXT on Manifest V3, with React, TypeScript, Radix UI Themes and Zustand above it. Around that sit the pieces every extension rewrites: a typed wrapper over chrome.storage, a type-safe message bus between popup, background and content scripts, useAuth and useStorage hooks, and i18n scaffolding present from the first commit rather than retrofitted in month six.
There is also a check that runs before you submit.
A grep gate looks for remote code in the built output, because this particular rule is invisible to a type-checker, a linter and a passing build alike. A green build tells you the extension compiles. It tells you nothing at all about whether a reviewer will accept it.
Then the honest part, which is why this is a template rather than a package.
CRXForge is a degit scaffold. You copy it and it becomes your repository. It is not published on npm, it is not a live extension in any store, and the demo interface is deliberately thin because it exists to be deleted.
That distinction matters more than it sounds.
A scaffold you copy is a scaffold you own, with no upstream that can shift under you and no version to keep up with. It also means you inherit its mistakes permanently, since there is no mechanism to push a correction into a copy somebody took in March.
What it cost is what every template costs.
It goes out of date. WXT moves, a store revises a policy, a dependency ships a breaking release, and a starter that was correct in one quarter is quietly not correct in the next.
So the useful way to read it is as a record of decisions rather than as code to trust indefinitely. Each compliance choice in it exists because the rule behind it is one browser stores state plainly, and the cost of finding that out late is a rewrite rather than an edit.
It builds for Chrome, Firefox and Edge. Safari is not a target. There is no iOS release behind any of this, because there is no Apple Developer account here.
MIT, at github.com/aoneahsan/crxforge.
Read the pre-submission checklist before the first commit rather than after the first refusal. Reading it early costs an afternoon, and reading it late costs the difference between a store queue you entered once and one you entered twice.
What it does, and what that costs to build
- Cross-browser builds for Chrome, Firefox, and Edge from one codebase
- Store-compliant Google sign-in via the Chrome Identity API
- Typed chrome.storage wrapper with a reactive useStorage hook
- Type-safe popup-background-content messaging bus
- Manifest V3 with strict CSP and minimal default permissions
- Bundled analytics pattern (Amplitude HTTP, Firestore REST, Sentry) with no CDN scripts
- Pre-submission remote-code grep gate and customize-it checklist
- React 19 + Radix UI Themes accessible popup and options pages
Built with
- WXT 0.20
- React 19
- TypeScript
- Radix UI Themes
- Zustand 5
- Manifest V3
- Chrome Identity API
- Vite
- ESLint 10
- Yarn 4
Worth knowing
- browser-extension
- manifest-v3
- wxt
- react
- typescript
- starter-template
- chrome-extension
- cross-browser
Who built CRXForge?
Ahsan Mahmood built it out of extensions he had already shipped. It is MIT licensed and lives at github.com/aoneahsan/crxforge, which is where you copy it from rather than install it. That provenance is the only real reason to prefer it over any other starter, because the compliance decisions in it are the ones a shipped extension has to make rather than the ones a starter can leave to the reader. A template written by somebody who has not shipped one looks identical until the constraint bites.
Is CRXForge an npm package?
No. It is a degit scaffold, copied with npx degit aoneahsan/crxforge, so it becomes your own repository from the first commit. It is not published on npm and it is not a live extension in any store, and saying that plainly is the whole point of this answer. What you get is ownership with no upstream to track. What you also get is permanence, because a fix made here later cannot reach a copy you took in March.
Does CRXForge work on iPhone?
No. It produces desktop browser extensions for Chrome, Firefox and Edge, and none of those is a phone target. Safari is not a target either, and there is no iOS release behind any of my work, because there is no Apple Developer account here and nothing I build has shipped to the App Store. If a Safari extension is what you need, this template will not get you there, which is better said on this page than found out during a build.
Why does Google sign-in go through the Chrome Identity API?
Because the Firebase Auth SDK fetches code at runtime, and a submission carrying it comes back. The Identity API keeps the entire sign-in path inside the file the reviewer downloaded, which is the single substitution that removes the most common reason an extension is refused. The analytics choices follow the same logic: Amplitude over HTTP, Firestore over REST and Sentry compiled in, none of them pulled from a CDN. A strict script-src of self is what turns that intention into something the browser enforces.
What do I have to replace before submitting?
The demo interface first, because it is deliberately thin and exists to be thrown away. After that the customise-it checklist covers the parts a template cannot decide for you: the permission set, the listing copy, the analytics keys and the extension's own identity. A grep gate runs before submission and looks for remote code in the built output, which matters because that rule is invisible to a type-checker, a linter and a green build alike.
https://aoneahsan.com/projects/com.aoneahsan.crxforge