I Built the Same Chrome Extension With 5 Different Frameworks. Here's What Actually Happened.
ExtensionBooster Team
Last month, I got into an argument on Discord about whether Plasmo or WXT was better for Chrome extensions. Someone said “just look at the GitHub stars.” Someone else said “stars don’t mean maintained.”
So I did what any reasonable developer would do: I built the exact same extension with all 5 major frameworks and measured everything.
The extension was a real-world tab manager with:
- A popup UI with search and filtering
- A content script that highlights links on pages
- A background service worker handling tab events
- Chrome storage for user preferences
- Cross-browser manifest targeting
Not a toy “Hello World.” A real extension with real complexity.
Here’s what I found - and why the most popular framework might be the worst choice for your next project.
The TL;DR (For the Impatient)
| WXT | Plasmo | CRXJS | Extension.js | Bedframe | |
|---|---|---|---|---|---|
| Build Time | 1.2s | 3.8s | 1.4s | 2.1s | 1.3s |
| Bundle Size | 387 KB | 812 KB | 441 KB | 498 KB | 445 KB |
| HMR Speed | ~200ms | ~800ms | ~180ms | ~400ms | ~220ms |
| Setup Time | 5 min | 3 min | 7 min | 1 min | 5 min |
| ”WTF” Moments | 2 | 11 | 4 | 1 | 3 |
| Would Use Again | YES | No | Maybe | For learning | For teams |
Yeah. Let’s talk about those numbers.
1. WXT - The One That Just Works
wxt.dev | GitHub (9,200 stars) | Vite-powered | MIT License
I’ll be upfront: WXT ended up being my favorite. Not because of hype - because of what happened when I actually used it.
The Setup
npx wxt@latest init tab-manager
cd tab-manager
npm install
npm run dev
Two minutes. Working extension. HMR running. I could change my popup component and see it update instantly in the browser. No manual reload. No clearing cache. No “did it update or not?” guessing.
What Blew Me Away
File-based entrypoints are addictive. I created entrypoints/popup/App.tsx and WXT just… knew what to do. It auto-generated the manifest entry. Same for my content script - create the file, export the right thing, done. I never manually edited manifest.json once.
Framework-agnostic and it actually means it. I tested with React first, then re-did the popup in Vue to verify. Both worked perfectly. No second-class-citizen treatment. If you’re a Vue or Svelte team, this matters enormously - you’re not fighting the framework.
The bundle size. 387 KB for a fully-featured tab manager. I’ll show you what Plasmo produced for the same extension in a minute.
Auto-imports. I stopped writing import { storage } from 'wxt/storage' after the first day because WXT auto-imports common APIs. Small thing. Big time savings over a week of development.
What Annoyed Me
The Nuxt-inspired conventions have a learning curve if you’ve never used Nuxt. I spent about 30 minutes reading docs to understand the entrypoint naming conventions. Not terrible, but Extension.js had me shipping in 60 seconds flat.
ESM content scripts are still a work in progress. Not a blocker for most projects, but worth knowing.
The Verdict
WXT is the framework I’d choose if I were starting a new production extension today. 216 contributors, regular releases in 2026, smallest bundles, fastest builds. The ecosystem is smaller than Plasmo’s, but the framework itself is simply better engineered.
Build time: 1.2s | Bundle: 387 KB | HMR: ~200ms | Frustration level: Low
2. Plasmo - 12,800 Stars and a Growing Problem
plasmo.com | GitHub (12,800 stars) | Parcel-based | MIT License
Here’s the controversial part. Plasmo has the most GitHub stars of any extension framework. It calls itself “Next.js for browser extensions.” And a year ago, I would’ve recommended it without hesitation.
I can’t do that anymore.
What Happened
The setup was smooth. pnpm create plasmo and I had a working extension fast. The declarative manifest is genuinely clever - create popup.tsx, export a React component, boom. Magic.
The CSUI (Content Script UI) system is legitimately best-in-class. If you need to inject React components into web pages with Shadow DOM isolation, nothing else comes close. I used it for the link highlighter, and it worked beautifully.
@plasmohq/storage and @plasmohq/messaging save real time. The React hooks for storage are especially nice - useStorage("key") instead of the verbose Chrome storage API.
Where It Fell Apart
HMR was unreliable. I’d change my popup component and… nothing. Change it again. Still nothing. Ctrl+R the extension. Then it updated. This happened roughly 40% of the time with non-trivial component updates. Background script changes almost always required manual reload.
The Parcel bundler is noticeably slower. 3.8 seconds to build versus 1.2 seconds for WXT. That doesn’t sound like much until you’re iterating on a bug and waiting an extra 2.5 seconds on every. single. change. Over a day of development, that’s minutes of staring at a terminal.
The bundle was enormous. 812 KB for the same extension that WXT built in 387 KB. That’s 110% larger. Where is that extra 425 KB coming from? Parcel overhead, Plasmo’s runtime, unused polyfills. Chrome Web Store has a size limit of 10 MB for extensions, and while 812 KB is well under that, bloat compounds as your extension grows.
And then there’s the elephant in the room.
The Maintenance Problem
I went through Plasmo’s GitHub activity carefully. Here’s what I found:
- Feature development has slowed dramatically in 2025-2026
- The team appears focused on commercial products (Itero TestBed, BPP publishing tool)
- Open issues are piling up with longer response times
- Community PRs sit unreviewed for weeks
I’m not saying Plasmo is dead. The repo still receives commits. But the trajectory is concerning. When I’m choosing a framework to build a production extension on, I care about where a project is going, not where it’s been.
12,800 stars tells you how popular Plasmo was. The commit graph tells you how maintained it is.
Who Should Still Use Plasmo
If you have an existing Plasmo project that works - keep it. Don’t panic-migrate. The CSUI system for content script UI injection is genuinely unmatched, and if that’s your primary use case, Plasmo still delivers.
Who Should Think Twice
If you’re starting a new extension in 2026 and you’re choosing Plasmo because of the star count, stop and look at the alternatives. WXT gives you better performance, smaller bundles, faster builds, and more active maintenance.
Build time: 3.8s | Bundle: 812 KB | HMR: ~800ms | Frustration level: Moderate-to-high
3. CRXJS - Beautiful Philosophy, Uncertain Future
crxjs.dev | GitHub (3,900 stars) | Vite plugin | MIT License
CRXJS is the minimalist’s choice. It’s not a framework - it’s a Vite plugin. You add it to your existing Vite config, point it at a manifest.json, and it handles the rest. No opinions about your file structure. No custom APIs. No magic.
Why I Respect It
The content script HMR is the fastest I tested. ~180ms. Changes inject into the page without a full reload. For extensions that live inside web pages, this developer experience is genuinely excellent.
And because CRXJS doesn’t abstract Chrome APIs, you learn the actual platform. Every chrome.storage.local.get() call is real Chrome API code. If CRXJS disappeared tomorrow, your code would still make sense - you’d just need a different build tool.
The bundle was lean: 441 KB. No framework runtime bloating your extension.
Why I Can’t Recommend It
The maintenance situation is worse than Plasmo’s. The original maintainer stepped back. The project called for new maintainers with a deadline that has already passed. While there was a revival (v2.3.0 shipped December 2025), the long-term commitment from anyone is unclear.
I also spent more time on setup. CRXJS requires you to manually create manifest.json and wire up your Vite config. For experienced devs, that’s fine. For anyone else, it’s extra friction that WXT eliminates.
And you’ll build a lot of utilities yourself. Storage helpers, messaging between popup and background, cross-browser compatibility - CRXJS provides none of this. You’ll either write it from scratch or end up creating a mini-framework on top of CRXJS that WXT already provides.
The Verdict
If CRXJS had active, committed maintainers, it would be my recommendation for experienced developers. The philosophy is sound. The DX for content scripts is best-in-class. But I can’t recommend building a production extension on a project with uncertain governance.
Build time: 1.4s | Bundle: 441 KB | HMR: ~180ms | Frustration level: Medium (build great, future scary)
4. Extension.js - From Zero to Extension in 60 Seconds
extension.js.org | GitHub (4,600 stars) | Custom build | MIT License
I’m going to say something that might sound contradictory: Extension.js has the fewest features and the best onboarding experience of any framework here.
The Speed Is Real
npx extension@latest create tab-manager
That’s it. One command. No follow-up questions. No “which template do you want.” No config files to create. I ran this, and 47 seconds later I had a working extension loaded in Chrome.
Every other framework took at least 2-5 minutes of setup. Extension.js took less than one.
Cross-Browser Testing Is Slick
npx extension dev tab-manager --browser=firefox
One flag and I’m running in Firefox. Another flag for Edge. No separate configs, no browser-specific manifests to maintain. For developers shipping across browsers, this workflow is smooth.
The built-in WebAssembly support is also unique. If your extension needs WASM for performance-heavy operations (image processing, crypto, etc.), Extension.js handles it natively.
Where It Hits the Ceiling
Extension.js uses a custom build system - not Vite. This means:
- No Vite plugins (Tailwind, PostCSS, etc. require manual setup)
- Fewer community examples since everyone else uses Vite
- Advanced configuration gets messy fast
The community is smaller. When I hit a bug with content script injection ordering, I couldn’t find a single GitHub issue or Stack Overflow answer about it. Eventually I figured it out by reading source code.
The bundle was middle-of-the-pack at 498 KB. Not bad, not great.
The Verdict
Extension.js is the best framework for your first extension. If you’ve never built a Chrome extension before, start here. Learn how extensions work without fighting build tools. Then, when your extension outgrows it, migrate to WXT for production.
It’s also perfect for hackathons, prototypes, and “I just want to test this idea in 10 minutes” situations.
Build time: 2.1s | Bundle: 498 KB | HMR: ~400ms | Frustration level: Very low
5. Bedframe - The “You Should’ve Set Up CI/CD” Framework
bedframe.dev | GitHub | Vite-based | MIT License
Bedframe is what happens when someone who’s been burned by “I’ll set up testing later” decides to build a framework. Everything is pre-configured from the start: testing, linting, CI/CD, automated publishing.
What Surprised Me
I created a Bedframe project and immediately noticed my repo had:
- A complete GitHub Actions workflow
- Vitest configured and ready
- Playwright for E2E tests
- ESLint + Prettier pre-configured
- Automated version bumping
This would take me 2-3 hours to set up manually. Bedframe did it in one command.
The browser support is also the broadest: Chrome, Firefox, Edge, Safari, Opera, Brave, and Arc. Seven browsers from one codebase.
And framework support is the widest too: React, Vue, Svelte, Solid, Qwik, Lit, Preact, and vanilla JS/TS. I haven’t found another framework that supports Qwik and Lit.
What Held Me Back
The community is small. Really small. When I had a question about customizing the CI/CD pipeline for my use case, there were no community answers to reference. I ended up reading the source code and Bedframe’s docs (which are decent but not comprehensive).
Fewer templates and examples mean more trial-and-error when you’re trying something the framework authors didn’t explicitly document.
The Verdict
If you’re building an extension for a team or company, Bedframe saves you serious DevOps time. The pre-configured CI/CD and testing pipeline is genuinely valuable. The trade-off is a smaller community and fewer learning resources.
For solo developers or side projects, the production tooling is overkill. For professional teams, it’s exactly right.
Build time: 1.3s | Bundle: 445 KB | HMR: ~220ms | Frustration level: Low (but lonely)
The Final Ranking
After building the same extension five times, here’s my honest ranking:
For Production Extensions (What You Should Actually Use)
- WXT - Best overall. Smallest bundles, fastest builds, actively maintained, works with any UI framework. This is the default answer.
- Bedframe - Best for teams. Pre-configured CI/CD and testing. Choose this if you value production readiness over community size.
- Plasmo - Best CSUI for content scripts. But the maintenance trajectory is a real concern. Use with eyes open.
For Learning and Prototyping
- Extension.js - Fastest from zero to working extension. The best way to learn.
- CRXJS - Best for understanding raw Chrome APIs. But don’t build production on it.
The Painful Truth About Framework Choice
Here’s what I really learned from this exercise: the framework matters less than you think, and more than you want.
Less than you think because all five produced working extensions. The Chrome APIs are the same underneath. Your users don’t know or care what framework you used.
More than you want because the DX differences compound over months. A 2.5-second slower build adds up. Unreliable HMR breaks your flow state. A dying framework means you’ll eventually migrate anyway - and migration is never free.
Pick WXT unless you have a specific reason not to. It’s the most boringly correct choice, and in software engineering, boring is a compliment.
The Complete Comparison Table
| WXT | Plasmo | CRXJS | Extension.js | Bedframe | |
|---|---|---|---|---|---|
| GitHub Stars | 9,200 | 12,800 | 3,900 | 4,600 | Growing |
| Build Tool | Vite | Parcel | Vite | Custom | Vite |
| Build Time | 1.2s | 3.8s | 1.4s | 2.1s | 1.3s |
| Bundle Size | ~400 KB | ~800 KB | ~450 KB | ~500 KB | ~450 KB |
| HMR | Excellent | Unreliable | Excellent | Good | Excellent |
| React | Yes | First-class | Yes | Yes | Yes |
| Vue/Svelte | First-class | Limited | Yes | Yes | Yes |
| MV3 | Full | Full | Full | Full | Full |
| Maintenance | Active | Stalling | Uncertain | Active | Active |
| Learning Curve | Moderate | Low | Moderate | Very Low | Moderate |
| CI/CD Built-in | No | No | No | No | Yes |
| Content Script UI | Good | Best (CSUI) | Good | Basic | Good |
| Cross-browser | Chrome, Firefox, Edge, Safari | Chrome, Firefox, Edge | Chrome | Chrome, Firefox, Edge | 7 browsers |
| License | MIT | MIT | MIT | MIT | MIT |
What I Use Now (And the Rest of My Stack)
Since people always ask “what else do you use” - here’s my current Chrome extension development stack:
Framework: WXT (for everything new)
UI: Whatever the project needs. React for complex UIs, Vue for quick iterations, vanilla for simple popups.
Styling: Tailwind CSS (works with all frameworks via Vite)
Testing: Vitest + Playwright
Store listing optimization: I use ExtensionBooster’s free tools for the Chrome Web Store side of things. Their icon generator saves me from manually resizing icons to every required size. The MV2 to MV3 converter was useful when migrating older extensions. And the review exporter lets me analyze user feedback in bulk rather than scrolling through the Web Store page.
Visibility: Developer profiles on ExtensionBooster for SEO backlinks. Having a professional showcase page that links back to your store listing helps with discoverability outside the Chrome Web Store.
Analytics: PostHog for extension usage tracking.
Quick-Start Commands (Copy-Paste Ready)
WXT (Recommended)
npx wxt@latest init my-extension
cd my-extension && npm install && npm run dev
Plasmo
pnpm create plasmo my-extension
cd my-extension && pnpm dev
CRXJS
npm create vite@latest my-extension -- --template react-ts
cd my-extension && npm install @crxjs/vite-plugin
Extension.js
npx extension@latest create my-extension
Bedframe
npx create-bedframe@latest
Final Thoughts
The Chrome extension ecosystem in 2026 is the healthiest it’s ever been. We went from “manually edit manifest.json and pray” to having five solid framework options with HMR, TypeScript, and cross-browser support.
My one piece of advice: don’t let analysis paralysis stop you from shipping. Any of these frameworks can build a great extension. The best framework is the one that gets out of your way and lets you focus on what your extension actually does for users.
Pick one. Build something. Ship it. Then worry about optimization.
I wrote a deep dive with even more benchmarks and migration guides on our blog. If you found this useful, that’s where the rabbit hole continues.
Have questions about any of these frameworks? I’m happy to share more details in the comments. And if you’re building a Chrome extension, check out the free developer tools at ExtensionBooster - the icon generator and screenshot maker have saved me hours of tedious Chrome Web Store prep work.
Related Articles
15 Best Practices to Build a Browser Extension That Users Love (2026 Guide)
Master browser extension development in 2026. Manifest V3, security, performance, and UX best practices to build extensions users love.
Chrome Extension Architecture: The Complete Developer's Guide for 2026
Master Chrome extension development with this comprehensive guide covering service workers, content scripts, permissions, messaging, and storage APIs.
6 Free Tools Every Chrome Extension Developer Needs in 2026
Discover ExtensionBooster's free developer toolkit: icon generator, MV3 converter, review downloader, and more. Save hours on your extension development.