Desde enero de 2023, Chrome exige Manifest V3 para todas las extensiones nuevas. En junio de 2024 extendió esa obligatoriedad a las existentes. Si tienes código de MV2, no funciona tal cual: los service workers han reemplazado a las background pages persistentes, el CSP es más estricto y la red se gestiona con declarativeNetRequest en lugar de webRequest.
Montar todo eso a mano con webpack o Vite sin ayuda es tedioso. Necesitas configurar el manifest.json, gestionar el bundling de varios entry points (popup, content script, background), añadir TypeScript si quieres tipado y conseguir que el hot reload funcione de verdad durante el desarrollo. Eso son horas de configuración antes de escribir ni una línea de lógica de la extensión.
Ahí es donde entran los frameworks. Hay tres que concentran la mayoría del uso en 2026: Plasmo, CRXJS y WXT. Cada uno toma decisiones distintas sobre cuánto te quiere guiar y cuánto control te deja. Te cuento qué hace cada uno y cuándo conviene usar uno u otro.
Plasmo: convención sobre configuración, estilo Next.js
Plasmo apareció en 2022 con una idea clara: hacer que crear una extensión Chrome se parezca a crear una app con Next.js. La estructura de ficheros define qué es cada cosa sin que tengas que configurarlo.
El fichero popup.tsx se convierte automáticamente en el popup de la extensión. El fichero content.ts se inyecta como content script. El fichero background.ts se registra como service worker. No hay que declarar nada en el manifest porque Plasmo lo genera a partir de los ficheros que encuentre.
my-extension/
??? popup.tsx # ? popup de la extensión
??? content.ts # ? content script
??? background.ts # ? service worker MV3
??? package.json
Para el popup puedes usar React, Vue o Svelte: Plasmo integra cada uno sin configuración adicional. También incluye su propia capa de storage con @plasmohq/storage, que sincroniza datos entre el popup, el content script y el background de forma transparente.
El lado menos cómodo de Plasmo
El problema de apostar tanto por convención es que cuando necesitas salirte del camino marcado, las cosas se complican. Si tu proyecto crece y necesitas una estructura de carpetas diferente, o quieres integrar un sistema de build que Plasmo no contempla, te encuentras luchando contra el framework en lugar de usarlo.
También es el más opinionado de los tres, lo que puede ser una ventaja al empezar pero un lastre cuando el proyecto madura.
Cuándo tiene sentido: proyectos con UI compleja en el popup, equipos que prefieren seguir convenciones establecidas y no quieren pensar en la configuración del build.
CRXJS: el plugin de Vite que no quiere ser un framework
CRXJS toma la dirección contraria. No es un framework completo: es un plugin de Vite que añade soporte para extensiones Chrome y Firefox al proceso de build habitual. Si ya conoces Vite, te sentirás en casa desde el primer momento.
La diferencia principal con Plasmo es que tú defines el manifest.json a mano y CRXJS se encarga del bundling y del hot module reload. No te impone ninguna estructura de carpetas ni ninguna convención sobre cómo organizar el código.
// vite.config.ts
import { defineConfig } from 'vite'
import { crx } from '@crxjs/vite-plugin'
import manifest from './manifest.json'
export default defineConfig({
plugins: [crx({ manifest })]
})
El HMR funciona de verdad: cambias el código del popup y ves el resultado sin reinstalar la extensión, algo que con el proceso nativo de Chrome es imposible. Y al ser solo un plugin de Vite, se integra con cualquier framework de UI que Vite soporte: React, Vue, Svelte, Solid, lo que uses.
El punto débil de CRXJS
El mantenimiento ha sido irregular. Hay periodos en los que las issues se acumulan sin respuesta y las actualizaciones tardan más de lo deseable. En 2026 sigue siendo una opción válida, pero si el mantenimiento activo es un criterio importante para ti, WXT tiene más actividad.
Cuándo tiene sentido: proyectos que ya usan Vite y quieren integrar el build de extensiones con el mínimo cambio posible.
WXT: el que más tracción tiene ahora mismo
WXT (Web Extension Tools) es el más nuevo de los tres y el que más ha crecido en 2026. Se inspira en Nuxt y Next.js como Plasmo, pero con una filosofía más flexible y soporte multi-navegador desde el principio.
La carpeta entrypoints/ es el núcleo del proyecto: ahí van el popup, los content scripts y el background, y WXT los detecta y los configura automáticamente.
my-extension/
??? entrypoints/
? ??? popup/
? ? ??? index.html
? ? ??? App.tsx
? ??? content.ts
? ??? background.ts
??? public/
??? wxt.config.ts
TypeScript está integrado desde el principio: el manifest, las APIs del navegador y los tipos de cada entry point están tipados automáticamente. El hot reload funciona sin reinstalar la extensión durante el desarrollo. Y el soporte multi-navegador es el más completo de los tres: Chrome, Firefox, Safari (con sus particularidades) y Edge.
Lo que hace diferente a WXT
El auto-import de APIs del navegador es una de las cosas más prácticas. En lugar de escribir chrome.storage.local.get() o browser.storage.local.get() dependiendo del navegador destino, WXT expone una API unificada que funciona en todos.
El proyecto tiene mantenimiento activo y una comunidad que ha crecido rápido. Para proyectos nuevos en 2026, es la opción más segura si no tienes ningún condicionante que te fuerce a elegir otra.
Cuándo tiene sentido: proyectos multi-navegador, equipos que quieren un tooling bien mantenido y con futuro claro.
Comparativa directa
| Característica | Plasmo | CRXJS | WXT |
|---|---|---|---|
| Multi-navegador (Chrome/Firefox/Safari) | Parcial (Chrome + Firefox) | Chrome + Firefox | Sí (incluye Safari) |
| Hot reload en desarrollo | Sí | Sí (HMR real) | Sí |
| TypeScript nativo | Sí | Vía Vite | Sí (con auto-tipos) |
| Compatibilidad con frameworks UI | React, Vue, Svelte | Cualquiera con Vite | React, Vue, Svelte, Solid |
| Actividad del proyecto en 2026 | Moderada | Irregular | Alta |
| Curva de aprendizaje | Baja (si conoces Next.js) | Muy baja (si conoces Vite) | Baja-media |
| Flexibilidad de estructura | Baja (muy opinionado) | Alta | Media-alta |
La estructura de una extensión MV3
Antes de meterte con cualquiera de los tres frameworks, conviene tener clara la anatomía de una extensión MV3. Son cuatro piezas principales.
- manifest.json: el punto de entrada. Declara versión, permisos, iconos y qué ficheros son cada cosa. Con
"manifest_version": 3obligatorio. - Background (service worker): reemplaza a la background page de MV2. Ya no es persistente: el navegador lo activa cuando hay trabajo y lo cierra cuando termina. No puedes guardar estado en variables globales entre activaciones.
- Content scripts: se inyectan en las páginas web que visita el usuario. Pueden leer y modificar el DOM, pero no tienen acceso directo a las APIs de Chrome que requieren permisos.
- Popup: la ventana que aparece al hacer clic en el icono de la extensión. Es básicamente una página web normal con acceso a las APIs de Chrome.
Los permisos en MV3 son declarativos: hay que listarlos todos en el manifest. Ya no puedes pedirlos dinámicamente en tiempo de ejecución como en MV2, lo que implica pensar con más cuidado qué necesitas antes de publicar.
{
"manifest_version": 3,
"name": "Mi extensión",
"version": "1.0",
"permissions": ["storage", "activeTab"],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html"
},
"content_scripts": [{
"matches": [""],
"js": ["content.js"]
}]
}
Si te interesa el lado JavaScript de todo esto, este artículo sobre JavaScript y el desarrollo web cubre la base del lenguaje con buena profundidad. Y si vas a usar TypeScript en tu extensión (que es lo recomendable con cualquiera de los tres frameworks), aquí tienes un repaso a las novedades de TypeScript que aplican directamente a proyectos de extensiones.
Safari y Firefox en 2026: el estado real
Safari ha mejorado su soporte de MV3 pero todavía hay diferencias con Chrome que dan problemas. La API declarativeNetRequest tiene algunas limitaciones en WebKit, y ciertos permisos se comportan de forma distinta. WXT es el framework que mejor gestiona estas diferencias con abstracciones específicas por plataforma.
Firefox implementó MV3, pero con sus propias particularidades en declarativeNetRequest: algunas reglas que funcionan en Chrome no tienen el mismo comportamiento en Firefox. Si quieres que tu extensión funcione en los dos, necesitas testearla en cada uno, no dar por hecho que un build que pasa en Chrome pasa en Firefox.
Para proyectos que necesiten soporte real en los tres navegadores, WXT es hoy por hoy la opción más práctica. Plasmo y CRXJS funcionan bien en Chrome y aceptablemente en Firefox, pero Safari queda fuera de su alcance sin trabajo extra.
Cuál elegir
Si vas a empezar un proyecto nuevo y no tienes condicionantes, WXT es la apuesta más sólida en 2026: mantenimiento activo, soporte multi-navegador y TypeScript bien integrado desde el primer día.
Si ya tienes un proyecto en Vite y solo quieres añadir soporte de extensión con el mínimo cambio, CRXJS encaja perfectamente. Es el más transparente de los tres y no te obliga a reorganizar nada.
Plasmo tiene sentido cuando el grueso del trabajo está en el popup con una UI compleja y el equipo viene de trabajar con Next.js. La convención acelera el arranque, aunque limita la flexibilidad después.
Imagen: Pexels / Pixabay
