Originally published on ExtensionLaunch.
Choosing between WXT vs Plasmo used to be a question of taste: Vite or Parcel, file conventions or React-first magic. In 2026 it's mostly a question of who is still shipping releases. Below is the short version of the comparison, the numbers behind it, and a six-step checklist if you decide to move an existing Plasmo project.
The numbers that decide it
All figures were checked in October 2026.
| WXT | Plasmo | CRXJS | |
|---|---|---|---|
| Type | Framework | Framework | Vite plugin |
| Bundler | Vite (6.3+, 7, 8) | Parcel | Vite (3 to 8) |
| Latest release | 0.21.4 (Aug 11, 2026) | 0.90.5 (May 17, 2025) | 3.0.0 (Sep 24, 2026) |
| Weekly npm downloads (Oct 12–18) | ~431k | ~361k | ~221k |
| UI frameworks | Any | React first, Vue/Svelte supported | Any |
| Store ZIPs | wxt zip | plasmo build --zip | No |
| Firefox sources ZIP | Automatic | No | No |
| Store submission | wxt submit (Chrome, Firefox, Edge) | BPP GitHub Action | No |
The line that matters is "Latest release." Plasmo still installs, still works and still has a big install base. But a roughly 17-month gap with no npm release means any bundler conflict or Chrome API change you hit is yours to work around.
Where the developer experience actually differs
Project layout. WXT discovers entrypoints from an entrypoints/ folder and keeps content script options inside the file with defineContentScript. Popup and options pages are HTML files that mount your app. Plasmo uses named files (popup.tsx, content.ts) with an exported config, and its popup can be a bare React component export. That last trick is Plasmo's nicest convenience.
Dev loop. Both reload on change. WXT launches a browser with the extension already installed and applies HMR to UI pages; Plasmo gives you live reload plus React HMR.
Extension APIs. WXT exports a browser object that prefers globalThis.browser and falls back to globalThis.chrome, so promise-based code runs without webextension-polyfill. Plasmo ships its own wrappers, @plasmohq/storage and @plasmohq/messaging.
Cross-browser builds. The flags look different:
# WXT: Firefox and Safari default to MV2, add --mv3 if you want MV3 wxt build -b firefox --mv3 wxt zip -b firefox # extension ZIP + sources ZIP for AMO # Plasmo plasmo build --target=firefox-mv2 --zip In code, WXT exposes import.meta.env.FIREFOX for per-browser branches, while Plasmo sets process.env.PLASMO_BROWSER.
Safari. wxt build -b safari writes to .output/safari-mv2 (or safari-mv3). You still run Apple's safari-web-extension-packager on that folder, because WXT doesn't generate the macOS/iOS wrapper app.
So which one?
- Starting a new extension: WXT. It's actively released, works with any UI library, and covers the whole path from build to store submission.
- Chrome-first Vite app, minimal tooling: CRXJS. It keeps
manifest.jsonas the source of truth and handles build and hot reload. You write your own zip and release script. - Existing Plasmo extension that works fine: no need to rush. Plan a migration when you hit a bundler conflict or need a fix that hasn't shipped.
Migrating from Plasmo to WXT in 6 steps
- Install
wxt, move every entrypoint intoentrypoints/, and fold named exports like Plasmo'sconfiginto WXT's default export. - Give popup and options pages an HTML file that mounts your React/Vue/Svelte app. WXT won't render a
.tsxpage file on its own. - Move
assets/*topublic/and swap Plasmo's CSUI forcreateContentScriptUi. - Rename env vars:
PLASMO_PUBLIC_*becomesWXT_*orVITE_*. - Replace
--tagwith--mode. Plasmo's remote code imports have no WXT counterpart, so install those dependencies from npm and bundle them. - Diff your old production
manifest.jsonagainst.output/*/manifest.json. Permissions, host permissions and thekeyfield must match, or users get a fresh permission prompt.
Your extension ID doesn't change. The ID belongs to the Chrome Web Store item, not the framework. Upload the new build to the same item with a higher version number and users get a normal update, with their settings and your reviews intact.
On bundle size, measure your own build rather than trusting anecdotes. WXT prints each output file's size, so a before/after check takes a minute. One 2024 migration (Jetwriter) reported its ZIP going from 700 KB to 400 KB, but your mileage depends on your code.
Key takeaways
- WXT and Plasmo are both MIT-licensed frameworks; the real difference in 2026 is release activity.
- WXT is the safer default for new projects and the only one of the three with automatic Firefox sources ZIPs and a built-in
wxt submitcommand for Chrome, Firefox and Edge. - CRXJS is a Vite plugin, not a framework, so plan your own packaging and publishing.
- Migrating keeps your extension ID; the step to get right is the manifest diff, so users don't see new permission prompts.
The original article has the full side-by-side, the FAQ and more detail on each step: WXT vs Plasmo framework comparison and migration guide. If you're getting close to shipping, ExtensionLaunch has more guides on launching and growing a browser extension.
Which framework are you building your current extension with, and what's the one thing that would make you switch?

