Is Plasmo still maintained
Check the signals yourself before you decide. These values come from npm and GitHub on 14 September 2026.
Plasmo still installs and still builds. Downloads stay high because existing projects keep it pinned. There has been no release in sixteen months. If your extension works today, you do not need to move. If you start a new extension, or you need Node.js 22, current Manifest V3 changes, or a bundler that still receives updates, this guide takes about ten minutes.
What changes, what stays
Stays the same: your React components, your Tailwind config, your tests, yourchrome.* API calls, and @plasmohq/storage if you use it (it wraps chrome.storage, which works the same here).
Changes:
- File-convention entrypoints (
popup.tsx,contents/*.ts) become explicit entries in a realmanifest.json. - The
manifestfield inpackage.jsonmoves intomanifest.json. plasmo dev/plasmo build/plasmo packagebecomeextension dev/extension build --zip.PLASMO_PUBLIC_*env vars becomeEXTENSION_PUBLIC_*.
Step 1: install Extension.js
Step 2: write the manifest
Plasmo generates the manifest frompackage.json plus file conventions. Extension.js treats manifest.json as the source of truth. Translate each convention:
HTML entrypoints are plain HTML files that load your React component. A popup looks like:
popup/index.html
.ts/.tsx in the manifest and in script tags. Extension.js compiles them at build time.
Step 3: convert content scripts
Plasmo reads the exportedPlasmoCSConfig for matches:
contents/inline.tsx
manifest.json
getRootContainer and anchors), mount your component with regular DOM code instead. Create a container element, append it where you anchored, and render into it. See Content scripts for the full pattern, including shadow DOM isolation for styles.
Step 4: env vars and messaging
- Rename
PLASMO_PUBLIC_*toEXTENSION_PUBLIC_*in your.envfiles and code. See Environment variables. @plasmohq/storagekeeps working as-is.@plasmohq/messagingdepends on Plasmo’sbackground/messages/*build convention. Replace handlers with standardchrome.runtime.onMessagelisteners in your background script. See Messaging.
Step 5: update package.json scripts
--target=firefox-mv2, Extension.js targets browsers directly:
dist/chrome and dist/firefox with browser-correct manifests and .zip archives ready for the Chrome Web Store and addons.mozilla.org.
Step 6: verify
--browser=firefox. Code that calls chrome.* runs on Firefox as written, because Firefox supports the chrome.* namespace natively. The --polyfill flag is for the other direction: it supplies browser.* on Chromium targets. extension dev applies it by default, and extension build leaves it off unless you pass the flag.
Common gotchas
- Icons in
assets/: Plasmo auto-generates icon sizes fromassets/icon.png. Extension.js uses whatever"icons"declares in the manifest, so export the sizes you need. See Icons. ~import alias: Plasmo aliases~to the project root. Map it intsconfig.jsonpaths, or switch to relative imports. See Path resolution.- Storage hooks:
useStoragefrom@plasmohq/storage/hookworks unchanged, because it only depends on React andchrome.storage.

