Development Chrome Extensions Frameworks Tools

I Built the Same Chrome Extension With 5 Different Frameworks. Here's What Actually Happened.

ET

ExtensionBooster Team

15 min read
Developer coding a Chrome extension with modern framework tools

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)

WXTPlasmoCRXJSExtension.jsBedframe
Build Time1.2s3.8s1.4s2.1s1.3s
Bundle Size387 KB812 KB441 KB498 KB445 KB
HMR Speed~200ms~800ms~180ms~400ms~220ms
Setup Time5 min3 min7 min1 min5 min
”WTF” Moments211413
Would Use AgainYESNoMaybeFor learningFor 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)

  1. WXT - Best overall. Smallest bundles, fastest builds, actively maintained, works with any UI framework. This is the default answer.
  2. Bedframe - Best for teams. Pre-configured CI/CD and testing. Choose this if you value production readiness over community size.
  3. Plasmo - Best CSUI for content scripts. But the maintenance trajectory is a real concern. Use with eyes open.

For Learning and Prototyping

  1. Extension.js - Fastest from zero to working extension. The best way to learn.
  2. 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

WXTPlasmoCRXJSExtension.jsBedframe
GitHub Stars9,20012,8003,9004,600Growing
Build ToolViteParcelViteCustomVite
Build Time1.2s3.8s1.4s2.1s1.3s
Bundle Size~400 KB~800 KB~450 KB~500 KB~450 KB
HMRExcellentUnreliableExcellentGoodExcellent
ReactYesFirst-classYesYesYes
Vue/SvelteFirst-classLimitedYesYesYes
MV3FullFullFullFullFull
MaintenanceActiveStallingUncertainActiveActive
Learning CurveModerateLowModerateVery LowModerate
CI/CD Built-inNoNoNoNoYes
Content Script UIGoodBest (CSUI)GoodBasicGood
Cross-browserChrome, Firefox, Edge, SafariChrome, Firefox, EdgeChromeChrome, Firefox, Edge7 browsers
LicenseMITMITMITMITMIT

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)

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.

Share this article

Related Articles