skip to content

What is a 'dynamic remote' in Module Federation, and how does resolving a remote's URL at runtime differ from declaring it statically in ModuleFederationPlugin's remotes config?

level: seniorimportance: should knowfreq 40%

answer

  1. static = string baked in at host build
  2. dynamic = resolved at runtime from a manifest/API
  3. Promise-based remotes config or manual script injection
  4. enables plugin marketplaces and multi-tenant variants
  5. no build-time check on a dynamic URL

basics

~20 s

A static remote's address is baked into the host when it's built. A dynamic remote's address is looked up while the app is actually running, for example fetched from a server, so the host doesn't need to be rebuilt just to point at a different remote.

solid answer

~50 s

Static remotes are declared as literal strings in a host's ModuleFederationPlugin remotes config, fixed at the host's own build time. Dynamic remotes instead resolve that same information — the container's URL and federation name — at runtime, typically by fetching a manifest, reading a config service, or being told by a feature flag, then either using a Promise-returning remotes entry or manually injecting a script tag and calling the loaded container's init/get functions directly. This decouples 'which remotes exist and where they live' from the host's own build, so a host doesn't need a rebuild and redeploy just because a remote's URL, environment, or tenant-specific variant changed. It enables plugin-marketplace-style extensibility, multi-tenant/white-label variants, environment-specific remote resolution, and per-cohort rollout of a remote's version — at the cost of weaker build-time safety, since a broken manifest entry only fails at runtime instead of being caught by the bundler.

go deeper

for a junior

Should grasp that a dynamic remote's location is looked up while the app is running instead of being fixed when it was built.

for a middle

Should be able to describe the Promise-based remotes config pattern or manual script-injection approach at a basic level.

for a senior

Should articulate the concrete use cases dynamic remotes unlock, such as multi-tenant, plugin marketplace, environment promotion, and rollout control, and the build-time-safety trade-off they introduce.

for a principal

Should reason about when to invest in dynamic remote infrastructure at all — the manifest service, validation, and observability it requires — versus when static remotes with a standard CI/CD pipeline are simpler and sufficient for the organization's actual number of remotes and deployment cadence.

## Static versus dynamic resolution A 'dynamic remote' is a remote whose location — the URL of its container/remoteEntry.js — is not baked into the host's Webpack config at build time, but is instead resolved at runtime, typically from an API call, an environment variable read at startup, or a manifest file fetched over the network. This sits in contrast to a 'static remote,' where the host's `ModuleFederationPlugin` config has a literal `remotes` entry such as { catalog: 'catalog@https://cdn.example.com/catalog/remoteEntry.js' } baked into the compiled bundle, meaning that URL is fixed the moment the host itself is built and cannot change without rebuilding and redeploying the host. ## How a dynamic remote is wired Mechanically, dynamic remotes are implemented by: - using a **Promise-based syntax** in the `remotes` config instead of a plain string, for example remotes: { catalog: () => new Promise(resolve => { fetch a config, then resolve the loaded container }) }. - or, more commonly, by manually performing the two steps the static path does automatically: injecting a script tag pointing at a remote-entry URL discovered at runtime, waiting for it to load and register itself on window under its federation name, then calling that container's `init(shareScope)` and `get(moduleName)` directly instead of going through a static `import('remoteName/module')` call resolved by the bundler. Newer tooling, including the Module Federation runtime package and manifest-driven loaders, wraps this pattern into a cleaner runtime API, letting a host register or swap remotes by URL at any point during the app's lifecycle, not only at startup. ## Why they exist The reason dynamic remotes exist is that a build-time-fixed remote URL reintroduces exactly the kind of coupling Module Federation is meant to remove: with static remotes, if a remote's deployment moves to a new environment, a new CDN path, or a new tenant-specific instance, every host that references it needs a rebuild and redeploy just to update a string, which defeats independent deployability for the host side of the relationship. Dynamic remotes decouple which remotes exist and where they live entirely from the host's build, letting that mapping live in something that can change without a host rebuild: a backend-served manifest, a feature-flag service, or per-environment configuration. ## What this unlocks This unlocks several patterns that static remotes cannot support well. - **Multi-tenant or white-label products** can serve a different remote implementation of the same federated slot per customer, resolved by looking up the tenant's ID in a manifest at runtime. - **Plugin-marketplace-style architectures** — a host application whose feature set is extended by third-party or internally-published 'apps' registered after the host has already shipped — become possible, because new remotes can be registered by adding an entry to a backend-served list rather than shipping a new host build. - **Progressive rollout and A/B testing** of a remote's version becomes a matter of changing what a manifest endpoint returns per user cohort, rather than coordinating a host redeploy. - **Environment promotion** across dev, staging, and production can point the same host build at different remote URLs per environment purely through runtime configuration. ## The cost The cost is added runtime complexity and a weaker build-time safety net: with static remotes, a build tool can at least statically see that a `remotes` entry exists, even though it still can't validate the remote's actual exposed API without extra tooling; with fully dynamic remotes resolved from a network call, there is nothing for the bundler or type checker to check against at all, and a broken or missing manifest entry only fails at runtime, in front of a real user, often as a generic 'container is not defined' or unhandled promise rejection rather than a clear error. Teams adopting dynamic remotes therefore usually pair them with - strong runtime error boundaries around each federated mount point, - a manifest schema validated at fetch time, - and staging environments that exercise the exact same manifest-resolution path production uses, since the failure mode that static remotes catch at build time, a typo'd URL, becomes a runtime incident for dynamic remotes if not otherwise guarded against. ## A concrete example A concrete example: an internal developer portal lets any team publish a 'widget,' a small federated remote, by registering its remoteEntry URL in a central catalog service; the portal's shell host fetches that catalog on load, and for each entry the user has access to, dynamically resolves and mounts the corresponding remote, so a new team's widget appears in the portal the moment they register it, with zero changes to the shell's own codebase or deploy pipeline.

  • Why would a multi-tenant SaaS product prefer dynamic remotes over static ones?
    Different tenants may need different implementations of the same federated slot — a custom branded checkout flow for one customer, a default one for others. A manifest keyed by tenant ID lets the same host build serve the correct remote per tenant without maintaining separate host builds per customer.
  • What extra safeguard should a team add when adopting dynamic remotes that static remotes get for free?
    Schema validation on the fetched manifest at the point it's retrieved, since there's no bundler-level check that a dynamically resolved URL or federation name is even well-formed, let alone that the remote it points to actually exposes what the host expects.
  • Can a host mix static and dynamic remotes in the same application?
    Yes — it's common to statically declare stable, core remotes owned by the same organization while dynamically resolving optional or third-party/plugin-style remotes whose set and location can change without a host rebuild.

A static remote is like a hardcoded phone number saved in your contacts that only updates when you manually edit your phonebook; a dynamic remote is like calling directory assistance every time to look up the current number before dialing — more flexible when numbers change often, but you're now depending on directory assistance actually working.

saying these in an interview costs you the question

  • conflates dynamic remotes with lazy-loading, when static remotes are already fetched lazily on first import
  • doesn't know dynamic remote resolution failures only surface at runtime
  • can't describe at least one real use case such as multi-tenant, plugin marketplace, or per-environment config
  • thinks dynamic remotes require a completely different container protocol

context