Chrome's native browser namespace: what cross-browser extension devs should do now
Chrome added a native, promise-based browser.* namespace in 148. Across the 235,887 Chrome extensions we track, here's what it changes — and what to do.

Every one of the 235,887 Chrome extensions we track in the catalog — a combined ~2.1 billion installs — got a second way to call the extension APIs back in Chrome 148, which shipped to the stable channel on May 5, 2026 (Chrome release notes 148, stable channel post). The native browser namespace alongside the old chrome one has been part of the platform for a few stable cycles now. If you write extensions and haven't acted on it yet, this is one of those quiet platform changes that makes your codebase smaller. Let me explain why.
The two-namespace mess we've all been living with
If you've only ever built for Chrome, you might not know there was a problem. Here's the short version.
Chrome's APIs have always lived under chrome.* and used callbacks: chrome.storage.local.get(keys, callback). Firefox and Safari followed the W3C WebExtensions direction and exposed the same surface under browser.*, but promise-based: await browser.storage.local.get(keys). Same functionality, two different names, two different calling conventions.
For years the workaround was a polyfill. You'd pull in Mozilla's webextension-polyfill, alias everything to browser, and get promises in Chrome too. It worked, but it's a dependency you ship, a thing that runs before your code, and one more line in your build that a new contributor has to understand. For a one-content-script extension, that's annoying overhead for what should be a language feature. (Most cross-browser frameworks bundle this shim for you — see how WXT, Plasmo and the rest compare.)
What Chrome 148 added
Since 148, Chrome exposes browser as a first-class namespace that mirrors chrome, and the methods return promises (browser namespace docs). So this:
chrome.storage.local.get(["token"], (result) => {
doSomething(result.token);
});becomes this:
const { token } = await browser.storage.local.get(["token"]);
doSomething(token);No polyfill, no aliasing, no callback pyramid. The chrome.* namespace isn't going anywhere — your existing code keeps working untouched — but browser.* is now the cleaner path, and it's the same name you already use in your Firefox build. One API surface, three browsers. That's the whole point.
Why this matters more than it looks
The headline isn't "promises in Chrome" — we've had those via polyfill forever. The headline is convergence. The single biggest tax on cross-browser extension development has been maintaining two mental models and a shim to bridge them. Chrome adopting browser.* collapses that.
It also arrived at a useful moment. The ecosystem is already mid-migration to Manifest V3 (see what Chrome's MV2 sunset actually removed), so a lot of you are touching your extension code anyway. Doing the browser.* switch in the same pass is close to free.
The backward-compatibility trap (read this before you delete the polyfill)
Here's where I have to play killjoy. The native browser namespace only exists in Chrome 148 and up. Your users are nowhere near all on 148+. As of this writing only about 30% of Chrome users run 148 or newer — roughly 70% are still on older builds with no native browser object at all (Chrome version usage share, caniuse.com / StatCounter). The catalog tells the same story from the other side: we count 24,660 Chrome listings (about 144M installs) that have dropped out of the store entirely, and the live install base spans years of browser versions. If you rip out the polyfill and ship raw browser.*, that ~70% gets a hard ReferenceError and a broken extension.
So treat this like any other platform-skew problem — old and new have to run side by side during the transition:
- Bump
minimum_chrome_versionto"148"in your manifest if you're comfortable dropping older users, then you can usebrowser.*directly and delete the shim. - Or keep the polyfill as a fallback for one or two more release cycles: it no-ops where the native namespace already exists, so you lose nothing by leaving it in while your user base ages forward.
- Don't assume
browseris defined just because your dev machine is on the latest Chrome. Feature-detect or set the floor explicitly.
Pick based on your install spread, not on what's tidiest. The tidy version breaks real users.
What to do this week
- Check your floor. Look at the Chrome versions your install base actually runs before you decide whether to require 148.
- New code, new namespace. Anything you write from today, write as
await browser.*. It's the same call you already make in Firefox. - Migrate incrementally. You don't need a big-bang rewrite —
chrome.*andbrowser.*coexist in the same extension. Convert files as you touch them. - Plan the polyfill exit, don't rush it. Keep it until your
minimum_chrome_versionclears 148, then drop the dependency and the build step with it. - Validate the manifest before you ship the change. My free Manifest V3 generator lets you set
minimum_chrome_versionand check the cross-browser keys against current store policy, and@extenshi/cli(npx @extenshi/cli) scans the packaged build before you upload — 3 scans free, one-time, prepaid packs past that.
This is the rare platform change that's all upside if you're patient about the version floor. Smaller bundles, one API surface, less glue code — and the only way to get it wrong is to forget that not everyone updates Chrome the day you do.
Numbers in this piece come from Extenshi's catalog: the most recent scan across our tracked Chrome Web Store listings, as of mid-2026. Counts shift as we re-scan, and we don't independently verify upstream vendor claims. If you think something here is off, tell us at [email protected].
Explore extension analytics → to see how your extension stacks up across browsers — or browse the catalog to check what you've got installed.
Related Articles
Extension frameworks in 2026: how to pick the right one for your next project
WXT, Plasmo, CRXJS, Extension.js, and Bedframe — five browser extension frameworks compared by build time, bundle size, and maintenance health in 2026.

We counted what Chrome's Manifest V2 sunset actually removed — and it wasn't the ad blockers
Everyone said Chrome's Manifest V2 deadline would kill ad blockers. Here's what the sunset actually stranded — and why Firefox is now the MV2 refuge.