Can one codebase ship web, Android and a browser extension?
Yes. One React and TypeScript repository becomes a web app, a Capacitor Android app, and a Manifest V3 extension, packaged three times for three stores.
Yes โ one React and TypeScript repository becomes a web app, an Android app packaged with Capacitor, and a Manifest V3 browser extension listed on the Chrome Web Store, Firefox Add-ons and Edge Add-ons.
The browser extension governs the other two. No remote code. Manifest V3 forbids it, so the shared code is written to the extension's rule from the first commit, long before anything is packaged for a store.
My name is Ahsan Mahmood and I work alone. Most of what I ship follows that one pattern rather than three teams and three repositories that drift.
Why do three codebases drift before anybody notices?
Because drift is silent until a fix has to land twice. Three separate builds of the same product each look correct on their own. Each passes its own tests. The gap usually shows up the first time a bug is reported on one surface, fixed there, then reported again a week later on another.
Nobody decides to drift.
It arrives one small divergence at a time: a validation rule tightened in the web app, a date format corrected in the Android build, a permission renamed in the extension.
By the time the three disagree enough to matter, no single commit caused it and there is nothing to revert. What the fix costs then is a reconciliation rather than a change.
What does a React app give up to ship as a Chrome extension?
Remote code. Manifest V3 allows none of it. Everything the extension runs has to sit inside the package the reviewer downloaded, so nothing is fetched at run time and executed.
That is not a detail of the extension build. It is a constraint on the shared code.
A shared layer that reaches for a script at run time works in a browser tab and works on Android. It fails review as an extension. So either the shared code is written to the extension's rules from the start, or the third surface turns out to be a rewrite rather than a packaging step.
No remote code is both the store rule and the reason these listings keep passing review.
What is shared, and what is built three times?
The product is shared and the packaging is not. One React and TypeScript codebase carries the interface, the data layer and the tests. Three thin packaging layers turn that into a web build, a Capacitor Android build, and a Manifest V3 extension.
ZTools is the one I would open first. It started as a web app with 20 tools for Zaions, my own company. There was no extension and no Android build.
Then I rebuilt it rather than extending it: the redesign brought the Android app, the browser extension and a docs site, all in that same pass. That rebuild was the cost.
A third surface is not a packaging decision made at the end.
The web app is past 550 tools. Roughly two years separate the first version from the one running now. ZTools has a page on this site and the live app is at ztools.zaions.com.
The Android half is the shorter story, and it has a post of its own. Google Play listings are packaged with Capacitor from the same web codebase rather than rewritten.
Two npm packages carry the pattern between products instead of being copied into each one: native-update and strata-storage. The first delivers over-the-air web-bundle updates to Capacitor apps, with checksum and signature verification and automatic rollback when a bundle fails to boot.
A package is how a decision stops being made twice.
How does one codebase reach the Chrome, Firefox and Edge stores?
Packaged three times, submitted three times. The three packages come out of one repository with no separate source tree behind any of them, and the differences between them are the manifest and the packaging layer. The product is written once; the package and the listing are made three times.
Check that on Video Controls Plus. It is listed on Chrome, Firefox and Edge. One repository sits behind all three.
Each listing is read by a reviewer who has not seen the other two. That is the part that does not compress, and it is the honest answer to anyone expecting one submission to cover three stores.
Writing those three listings and answering three reviewers is part of what browser-extension engineering covers.
Why are there no install numbers on this page?
No download or install counts, anywhere on this site. They fluctuate, nobody can verify them, and the number is never the interesting part.
An earlier version of this site made claims about my own work that a reader could not check. One was that a browser extension of mine had thousands of users. Another was that I was an expert in several things. None of them were verifiable.
What that costs is not the one unchecked sentence; it is every true sentence sitting beside it.
Those claims came down. What replaced them, when I rebuilt this site, is the rule the rest of this page runs on: a claim resolves to something you can open, or it is not made. So the three listings above are linked and the install counts behind them are not.
What does one codebase not give you?
There is no single release. A web fix is live when I deploy it. An Android build is live when the Play listing updates, and an extension update is live once each store has approved it, which is three clocks running on one commit.
So a fix lands everywhere at once in the repository and reaches people at three different times. That gap on the Capacitor side is what native-update exists for.
It does not give you three separate arguments with three reviewers. Because all three packages come out of one repository, a change asked for by one store is a change made in the code the other two ship. The strictest reviewer edits the product that every user of the other two gets.
Nor does it give you one set of store paperwork. Android brings its own, named in the post that owns that half. The extension brings a listing on each of three stores. None of that is shared just because the codebase is.
That price is paid up front.
One limit points the other way. The web app inherits the extension's rules even where a browser tab would have allowed more.
Which of your surfaces is strictest is usually what to settle first, because that is the surface your shared code obeys for the life of the product. When the Android half is what you are sizing, what Capacitor actually costs is the next thing to read.
Tell me through the contact form what you are building and which of the three surfaces it has to reach. The first call is thirty minutes and costs nothing.
Can a Chrome extension and an Android app really share a codebase?
Yes, and the sharing is real, not partial. Both are packaged out of one React and TypeScript repository: the Android app through Capacitor, the extension through Manifest V3. What is shared is the interface, the data layer and the tests. What stays separate is each surface's packaging layer, its entry point and its store listing.
What does Manifest V3 forbid?
Remote code. Nothing an extension runs may be fetched at run time, so everything it executes sits inside the package the store reviewed. In a shared codebase that rule does not stop at the extension: it sets the ceiling for the web app too, which is written to the extension's limit even where a browser tab would have allowed more.
Does a browser extension need its own backend?
No. The extension is the same codebase as the web app, so it talks to whatever the web app already talks to. The Manifest V3 rule is about code rather than data: the extension may read from an interface it does not ship, and it may not download something to run.
What happens when one store rejects the build?
Nothing happens to the other two listings; they are separate queues. A rejection is answered in the shared codebase, so the correction reaches all three packages the next time they are submitted. How long a review takes is not a figure I publish. The store sets that clock, and I do not.
Which extension stores do you actually publish to?
The Chrome Web Store, Firefox Add-ons and Edge Add-ons. Video Controls Plus is listed on all three; ZTools is on the Chrome Web Store, alongside its Google Play listing and its web app. Both resolve from their pages on this site, so the claim is checkable rather than asserted. Each listing is written separately, for a reviewer who has not read the other two.
https://aoneahsan.com/blog/one-codebase-web-android-and-a-browser-extension