wandres.dev
MODULE FEDERATION · microfrontends

Qué es Module Federation: integrar en runtime

La idea que rompe el molde del bundler: cargar código de otra aplicación —construida, versionada y desplegada por separado— en el momento en que el navegador lo necesita, no cuando compilas. Module Federation convierte el grafo de módulos, hasta ahora cerrado en un build, en una red abierta que cruza fronteras de despliegue.

⏱ 16 min

Todo lo que has visto en este track asumía una frontera sagrada: el grafo de módulos se cierra durante el build. El bundler resuelve cada import, agrupa todo en chunks y sella el resultado; lo que no estaba presente al compilar, sencillamente no existe para esa aplicación. Module Federation rompe esa frontera. Permite que una aplicación cargue, en tiempo de ejecución, código de otra aplicación distinta —compilada, versionada y desplegada por su cuenta— como si fuera un módulo propio. El grafo deja de ser un archipiélago cerrado y pasa a ser una red que cruza fronteras de despliegue.

🎯 Al terminar esta lección sabrás
  • Distinguir la integración en build de la integración en runtime, y por qué esa distinción lo cambia todo.
  • Entender el contenedor federado y su manifiesto como la unidad que se publica y se consume.
  • Ver por qué esto no equivale a un paquete npm y qué problema resuelve que un paquete no puede.
  • Situar Module Federation en el ecosistema de 2026: Rspack, la runtime 2.0 y el plugin de Vite.

La frontera que un bundle no cruzaba

Durante todo este track, integrar código ajeno significó una cosa: instalarlo como dependencia y dejar que el bundler lo absorbiera. Cuando escribes import de una librería, esa librería entra en tu grafo, se resuelve, se poda por tree-shaking y termina fundida en tus chunks. La integración ocurre en el build, y el artefacto que despliegas es autosuficiente: contiene todo lo que necesita, congelado en el instante en que compilaste.

Esa autosuficiencia tiene un precio que rara vez se nombra: para cambiar cualquier trozo de código, hay que reconstruir y redeplegar la aplicación entera. Si diez aplicaciones comparten un botón y el botón cambia, las diez se recompilan. El grafo cerrado es cómodo, pero acopla el ciclo de vida de todo lo que vive dentro de él.

Module Federation introduce una segunda vía: la integración en runtime. En lugar de fundir el código ajeno en tu build, tu aplicación lo va a buscar cuando lo necesita, mientras el usuario navega. El código remoto vive en otro servidor, se construyó con otro pipeline y se desplegó en otro momento. Para tu aplicación es un import más; por debajo, es una petición de red a un artefacto que nunca formó parte de tu compilación.

// Parece un import local, pero "remoteUI" vive en OTRO despliegue.
// El navegador lo resuelve en runtime, no el bundler en build.
const { Boton } = await import("remoteUI/Boton");

La palabra clave es cuándo. Un paquete npm se integra al compilar; un remote federado se integra al ejecutar. Ese desplazamiento en el tiempo es toda la idea, y de él se derivan tanto los poderes como los peligros que veremos en el resto del nivel.

El contenedor y su manifiesto

Para que una aplicación exponga código a otras, no basta con desplegarla: tiene que publicar un contenedor federado. El contenedor es un pequeño punto de entrada —clásicamente un archivo remoteEntry.js, y en la runtime 2.0 un manifiesto mf-manifest.json— que describe qué módulos ofrece y cómo cargarlos. Es el índice público del remote: quien lo lea sabe qué puede pedir y dónde están los chunks reales.

El protocolo por debajo es deliberadamente simple. El contenedor expone dos operaciones: una para inicializarse compartiendo el ámbito de dependencias comunes, y otra para pedir un módulo concreto, que devuelve una fábrica perezosa.

// El protocolo del contenedor, simplificado y a mano.
await __webpack_init_sharing__("default");
const container = window.remoteUI;                 // vino de remoteEntry.js
await container.init(__webpack_share_scopes__.default);
const factory = await container.get("./Boton");    // pide un modulo expuesto
const Modulo = factory();                            // lo evalua bajo demanda

Nadie escribe esto a mano. El bundler reescribe tus import("remoteUI/Boton") en esta danza de init y get, y la runtime moderna la envuelve en una sola llamada legible. Pero conviene haber visto el mecanismo desnudo una vez: te recuerda que un remote no es magia, sino un archivo índice y un puñado de chunks servidos por HTTP.

📝
La runtime esconde el protocolo

En Module Federation 2.0 no tocas init ni get. Cargas un módulo remoto con loadRemote, y la runtime se encarga de leer el manifiesto, negociar dependencias compartidas, descargar el chunk correcto y evaluarlo. El protocolo del contenedor sigue ahí debajo, pero tu código habla un idioma mucho más limpio.

import { init, loadRemote } from "@module-federation/runtime";

init({
  name: "host",
  remotes: [
    { name: "remoteUI", entry: "https://cdn.example.com/remoteUI/mf-manifest.json" },
  ],
});

const { default: Boton } = await loadRemote("remoteUI/Boton");
flowchart LR
subgraph Build tradicional
  S[tu codigo] --> B[bundler]
  L[librerias] --> B
  B --> G[un grafo cerrado y sellado]
end
subgraph Runtime con federacion
  H[host] -->|carga en runtime| M[manifiesto del remote]
  M --> C[chunks del remote desplegados aparte]
end
style G fill:#a6e3a1,color:#11111b
style H fill:#cba6f7,color:#11111b
style M fill:#f9e2af,color:#11111b
style C fill:#89b4fa,color:#11111b

No es un paquete npm

La reacción natural es preguntarse en qué se diferencia esto de publicar un paquete. La respuesta está, otra vez, en el cuándo. Un paquete npm compartido obliga a un ritual: publicas una versión nueva, y cada aplicación que lo consume debe actualizar su package.json, reinstalar, recompilar y redeplegar para incorporar el cambio. El código nuevo viaja por el ciclo de build de cada consumidor. Con diez consumidores, son diez pipelines que hay que orquestar para propagar un arreglo.

Un remote federado invierte ese flujo. Publicas el remote una sola vez en su URL, y los consumidores recogen la versión nueva en su siguiente carga, sin recompilar nada. El código nuevo no viaja por el build de nadie: viaja por la red, en el instante en que el usuario lo pide.

Aspecto Paquete npm Remote federado
Integración en build en runtime
Propagar un cambio recompilar cada consumidor redeplegar solo el remote
Acoplamiento de ciclo de vida alto bajo
Coste un chunk más en cada bundle una petición de red
Falla si el origen cae no, ya está dentro sí, hay que preverlo

Esa última fila anticipa el precio: al mover la integración a runtime, mueves también los fallos a runtime. Un paquete roto se detecta al compilar; un remote roto se detecta cuando el usuario ya está en la página. Todo el poder de la federación —desplegar de forma independiente— viene con esa contrapartida, que estudiaremos a fondo en la última lección del nivel.

ℹ️
De Webpack 5 a la runtime 2.0

Module Federation nació en Webpack 5, de la mano de Zack Jackson, en 2020, como plugin del bundler. En 2026 el centro de gravedad se ha desplazado: @module-federation/enhanced lo lleva a Rspack —el bundler en Rust—, la runtime 2.0 lo desacopla del empaquetador con una API programable de plugins, y @module-federation/vite lo trae al mundo ESM de Vite. En paralelo, Native Federation reimplementa la idea sobre import maps y ESM estándar, sin depender de ningún bundler concreto.

🦀

Rspack y enhanced

El plugin @module-federation/enhanced sobre Rspack ofrece la federación más completa y rápida de 2026, con manifiesto, tipos y DevTools propias.

⚙️

Runtime 2.0

Una librería de runtime independiente del bundler, con loadRemote, registro dinámico de remotes y hooks para interceptar cada carga.

Vite y ESM

@module-federation/vite y Native Federation llevan la federación al terreno de los módulos nativos y los import maps.

Federar es abrir el grafo a costa de mover los fallos al runtime

La tesis profunda de Module Federation es que el grafo de módulos no tiene por qué terminar en la frontera de tu build. Durante toda la historia del empaquetado, esa frontera fue un axioma: compilas, sellas, despliegas un artefacto que se basta a sí mismo. Federar cuestiona el axioma y propone un grafo que se extiende más allá del despliegue, cosido en runtime a partir de piezas que nadie compiló juntas. El poder que esto libera es real y concreto: equipos que publican su parte sin coordinar una release monolítica, arreglos que llegan a producción sin recompilar a los consumidores, una interfaz común que se actualiza en un solo sitio. Pero conviene ver con la misma claridad lo que se paga a cambio, porque no hay comida gratis en arquitectura. Al desplazar la integración del build al runtime, desplazas también la detección de errores: lo que antes rompía la compilación —un módulo que falta, un tipo que no encaja, una versión incompatible— ahora rompe la pantalla del usuario. Cambias la certeza estática del bundle cerrado por la flexibilidad dinámica de la red, y con ella heredas todo lo que la red tiene de frágil: latencia, caídas, versiones que dejan de encajar sin que nadie recompilara nada. Module Federation no es, por eso, una técnica de optimización ni un truco de rendimiento; es una decisión de arquitectura sobre dónde vive la frontera de integración y quién paga cuando algo al otro lado cambia. Entenderla como lo que es —un traslado de complejidad del build al runtime, del equipo al sistema— es lo que separa a quien la adopta porque resuelve un problema organizativo concreto de quien la adopta porque suena moderno y luego pasa meses depurando fallos que un monolito jamás habría tenido.

⚔️ Cruza la frontera del build
  1. Monta dos apps con @module-federation/vite o Rspack: una que exponga un componente y otra que lo consuma en runtime.
  2. Abre la pestaña de red del navegador y localiza la descarga del mf-manifest.json y del chunk del componente remoto; observa que ocurre al cargar, no al compilar.
  3. Cambia el componente en el remote, redepliégalo sin tocar el host, recarga el host y confirma que ves la versión nueva.
  4. Apaga el servidor del remote y recarga el host: observa cómo un fallo que antes sería de build ahora es de runtime.
  5. Escribe en una frase tu propia definición de la diferencia entre integrar en build e integrar en runtime.