Skip to content
extension/start

Migrating From an Archived Chrome Extension Boilerplate in 2026 (React-Vite and Friends)

The popular React chrome extension boilerplates were archived in early 2026. What that means, what MV3 keeps breaking, and a realistic path to a maintained WXT-based setup.

TL;DR: The two most-starred React chrome extension boilerplates were both archived in February 2026: lxieyang/chrome-extension-boilerplate-react (Feb 2) and Jonghakseo/chrome-extension-boilerplate-react-vite (Feb 14). Meanwhile MV2 stopped running on stable Chrome in July 2025, and the store delists remaining MV2 items after August 31, 2026. Migrating means: move to a maintained toolchain (WXT), then fix the things MV3 keeps breaking: service worker lifecycle, timers, remote code, storage. Your React components mostly survive; your background architecture mostly doesn’t.

For years, “start a React chrome extension” had a default answer: clone one of two GitHub boilerplates. As of this year, both are frozen. Here’s the factual state, what actually breaks when you migrate, and a realistic order of operations.

The state of the boilerplates (verified July 27, 2026)

  • Jonghakseo/chrome-extension-boilerplate-react-vite (~4.9k stars, React + Vite + Turborepo): archived by the owner on February 14, 2026; the repository is read-only.
  • lxieyang/chrome-extension-boilerplate-react (~3.9k stars, React 18 + webpack 5): archived on February 2, 2026; read-only. It did adopt MV3 before archiving, with an MV2 legacy branch.

No judgment attached: these were generous, widely used projects, and archiving is what responsible maintainers do when they stop. But an extension toolchain is not a library you can pin and forget: Chrome ships a new stable roughly every four weeks, review policies move, and the MV2 sunset timeline shows how much has changed just recently: MV2 extensions stopped running for stable-channel users in July 2025 (Chrome 138), and remaining MV2 items are removed from the Chrome Web Store after August 31, 2026. An archived boilerplate can’t respond to any of that.

What MV3 keeps breaking (and archived docs won’t tell you)

If your extension grew up on an older boilerplate, these are the failure classes to expect; they’re the same ones every migrator hits:

1. The background is ephemeral now. MV2 background pages ran forever; the MV3 background service worker is killed after ~30 seconds idle and revived by events. Everything downstream follows: module globals are a cache, not state (chrome.storage is the truth); setTimeout/setInterval don’t survive, so recurring work moves to chrome.alarms; and every event listener must be registered synchronously at the top level of the worker; a listener registered after an await can miss the very event that woke the worker. Keepalive hacks to keep the worker alive are a policy violation, not a workaround.

2. Remote code is banned. Loading scripts from a CDN, eval, string timers: all disallowed in MV3. Old boilerplate patterns like injecting a remote analytics snippet fail review now. Remote data (JSON config) remains fine.

3. One missing permission kills everything. A chrome.* API without its manifest permission is undefined; a module-scope throw takes down the entire background graph, and every message from every surface hangs forever. When you hand-edit manifests across a migration, this is the bug you’ll lose an afternoon to.

4. Storage outlives your code. chrome.storage persists across updates; there is no fresh-install reset for existing users. If your migration changes any stored shape, ship versioned migrations, not scattered “if old shape” checks.

Mapping old-boilerplate concepts to WXT

WXT is the actively maintained successor to the role those boilerplates played: Vite-based, cross-browser, with the manifest generated from typed config. (Other tools exist, like CRXJS and Plasmo, but WXT is what ExtensionStart builds on, so it’s what this table maps to.)

Old boilerplate concept WXT equivalent
Hand-edited manifest.json (or a template copied at build) Generated from wxt.config.ts, a per-browser function; you never edit a manifest
src/pages/popup, options, etc. + custom webpack/Vite entries Filesystem convention: entrypoints/popup/, entrypoints/options/, entrypoints/background/, entrypoints/content/
Custom HMR / “reload extension” plugins Built-in dev mode with reloading
Chrome-only build wxt build -b firefox: per-browser manifests (Firefox needs its own: no offscreen, no sidePanel)
Copy-webpack tricks for icons/assets public/ + convention
Background “page” with persistent state entrypoints/background/ service worker, plus the lifecycle discipline above, which no build tool can do for you

That last row is the honest one: WXT fixes the toolchain; the architecture (storage-backed state, top-level listeners, alarms, typed messaging between surfaces) is still on you.

A realistic migration order

Done in this order, each step leaves you with a working extension:

  1. Inventory first. List every entrypoint (popup, options, content scripts, background), every permission and host permission, and every stored key with its shape. Permissions are where silent breakage hides.
  2. Stand up WXT beside the old repo. Fresh WXT project; port manifest.json into wxt.config.ts. Diff the generated manifest against your old one until the permission sets match exactly.
  3. Move UI surfaces. Popup/options React components usually port almost unchanged; they’re just pages. Content scripts move into entrypoints/content/; if they inject styled UI, move to shadow-root mounting so host-page CSS can’t bleed in (and note that rem inside a shadow root resolves against the host page’s root font size; convert to px at build).
  4. Rewrite the background deliberately. This is the real work. Listeners to the top level; state into chrome.storage with hydration awaited inside handlers; timers onto chrome.alarms; ad-hoc runtime.sendMessage calls onto one typed messaging layer.
  5. Add storage migrations for any shape you changed, versioned and numbered; your existing users’ storage arrives as-is.
  6. Test against reality. Unit tests won’t catch a missing manifest permission or a dead service worker; drive the built extension with Playwright at least for install, background-alive, and each surface rendering.

What a maintained kit buys you

Being direct about the trade: you can absolutely do the migration above by hand onto plain WXT; it’s a good path, and for a simple extension it’s plenty. What a maintained commercial kit like ExtensionStart adds on top is the part after the toolchain: the background architecture pre-built with the lifecycle rules enforced by lint, typed messaging, versioned storage migrations, MV3-correct Firebase auth and Stripe billing, an e2e suite that already checks the failure classes in this guide, and (the reason this article exists) someone whose job is updating all of it when Chrome moves again. If your extension earns money or plans to, that’s the calculation to make; if it’s a weekend project, plain WXT will serve you well.

Frequently asked questions

Is chrome-extension-boilerplate-react-vite still maintained?

No. The Jonghakseo/chrome-extension-boilerplate-react-vite repository (~4.9k stars) was archived by its owner on February 14, 2026 and is now read-only. The older lxieyang/chrome-extension-boilerplate-react (webpack, ~3.9k stars) was archived on February 2, 2026.

Can I keep shipping an extension built on an archived boilerplate?

Yes, for a while; archived code still compiles. The risk is drift: no fixes when Chrome changes review requirements or APIs, no dependency updates when vulnerabilities land, and no answers when a new Chrome release breaks the build tooling. You inherit the maintenance the moment you need any of that.

Do I still need to migrate off Manifest V2?

That ship has sailed: MV2 extensions stopped running for regular users on stable Chrome in July 2025 (Chrome 138), and Google removes remaining MV2 items from the Chrome Web Store after August 31, 2026. Any migration you do today is MV3-to-better-MV3, not MV2-to-MV3, unless you're on a very old boilerplate branch, in which case MV3 is step zero.

What is WXT and why do modern kits use it?

WXT is an actively maintained, Vite-based framework for browser extensions. It generates the manifest from typed config (per-browser), maps a filesystem entrypoints/ convention to extension surfaces, handles dev-mode reloading, and builds for Chrome and Firefox from one codebase: the plumbing every archived boilerplate hand-rolled.

Why does my background script's state keep disappearing in MV3?

The MV3 background is a service worker that Chrome terminates after roughly 30 seconds of inactivity. Module-level variables reset on every wake. Persist state in chrome.storage, register all event listeners synchronously at the top level, and replace setTimeout/setInterval with chrome.alarms for anything that must outlive an activation.

How long does migrating a boilerplate-based extension actually take?

For a typical small extension: a day or two to move surfaces and manifest onto WXT, and another stretch for the background rewrite if your old code assumed a persistent page; that's where real bugs live. React components usually port nearly unchanged; the service-worker discipline is the work.