Static or Dynamic Microfrontends?
At first, the distinction between static and dynamic microfrontends looks like a configuration detail. In one variant, the address of a remote is written into a file; in the other, it is loaded from a manifest. Technically, the difference can indeed be described that briefly.
Architecturally, however, that description falls short.
The binding point determines when the decision is made about which remote a host will use. This affects whether a new mapping requires a new host build, how environments differ, how versions are activated or rolled back, and which failures can occur only in the running system.
The important question is therefore not only:
What are static and dynamic binding?
But rather:
When should the decision be made about which remote a host actually uses?
The later this mapping is defined, the greater the operational freedom. At the same time, more decisions, failure modes, and combinations must be handled not during the build, but during deployment or at runtime.
Static Does Not Describe When the Code Runs
Section titled “Static Does Not Describe When the Code Runs”A common misunderstanding needs to be cleared up immediately: a statically bound microfrontend does not have to be part of the host bundle.
Even with static binding, the remote code may be loaded over the network only when users navigate to it, open a functional area, or perform a particular interaction. In this context, static does not describe when the code is loaded or executed.
Static describes when the host receives its remote mapping.
With static binding, the host already knows when its artifact is created which remotes exist and under which address, or according to which rule embedded in the artifact, they are resolved:
Host build │ └── knows: tasks → https://tasks.example.com/remote-entry.jsThe host does not have to build the remote itself. It merely knows the mapping between a logical name and a reachable entry point.
With dynamic binding, the host artifact does not yet contain this specific mapping. It is determined later, for example during deployment, when the application starts, or through runtime configuration:
Host artifact │ ▼Runtime configuration │ └── resolves: tasks → version or URL for this contextHere, too, the actual remote code is loaded at runtime afterwards. The difference comes before that: only after its build does the host learn which artifact it should load.
Outside this narrower definition, the term “static microfrontend” is sometimes also used for code that is fully integrated into an application during the build. That is a different distinction. In this article, static and dynamic describe only the binding point between host and remote.
Static Binding Keeps the Composition Manageable
Section titled “Static Binding Keeps the Composition Manageable”With static binding, the host artifact knows the intended remote names, their addresses, or fixed rules for resolving them. The composition of the system is therefore defined early.
This may mean that a host configuration file contains a complete URL. It may also mean that the host knows only a stable name that is translated into an address according to a rule embedded in the artifact. What matters is that this information has already been defined when the host artifact is created.
The main advantage is not that static binding is necessarily particularly easy to implement. Its real advantage is a smaller number of moving parts in the running system.
There is no additional manifest whose availability, format, and cache behavior must be monitored. No registry is needed to decide at runtime which version of a remote should be delivered. The topology can be understood from the host artifact and its configuration.
This creates fewer possible production combinations. If a defined host has been released with three defined remotes, that product state can be reproduced relatively clearly. With appropriate validation, invalid names or addresses can be detected during the build, integration, or release process instead of only in a user’s browser.
This is true, however, only when the referenced artifacts are immutable or the artifact actually delivered is recorded. A stable URL with replaceable content is not automatically reproducible.
Especially in systems that are deliberately released as a coherent product, this predictability can be valuable. A shared release state is then not an accidental technical restriction, but a controlled outcome.
Static binding also reduces platform overhead. A team does not need dedicated infrastructure for remote manifests, release channels, or context-dependent resolution rules as long as there is no actual need for them. Less infrastructure does not automatically mean less architecture. It may simply mean that the architecture introduces only the variability the product genuinely requires.
The limitations are equally real. If a remote address embedded in the host changes, a new host build may be necessary. Adding a new remote name or removing an existing one will often require an adjustment to the host configuration as well. If addresses differ between test, staging, and production environments, using exactly the same host artifact everywhere becomes more difficult.
Purely static binding also has limited support for regional or tenant-specific mappings. If Italy should already use version 2 of a remote while Germany remains on version 1, that distinction must be made elsewhere or represented by different host artifacts.
This does not mean, however, that statically bound remotes can only be deployed together with the host.
If the host knows a stable address such as
https://tasks.example.com/remote-entry.jsthe responsible team can continue to publish new artifacts behind that address. The host does not need to be rebuilt as long as the name, address, and integration contract remain stable.
Static binding therefore does not automatically prevent independent remote deployments. It merely means that the mapping at the host level remains unchanged.
Dynamic Binding Defers the Decision
Section titled “Dynamic Binding Defers the Decision”With dynamic binding, the concrete remote mapping is determined only after the host build. The host artifact may still know the functional role of a remote, but not yet its final address or version.
The mapping may come from a manifest:
{ "tasks": "https://cdn.example.com/tasks/2.1.0/remote-entry.js", "members": "https://cdn.example.com/members/1.7.3/remote-entry.js"}It may equally be resolved by a registry, deployment configuration, a gateway, a CDN rule, or tenant configuration. The specific mechanism is not what matters. What matters is that the mapping can be changed without creating the host artifact again.
This allows the same host artifact to be used in multiple environments. The test environment may point to different remote addresses than production even though both use the same host. New remote versions can be activated by changing a mapping. A rollback may simply consist of pointing that mapping back to the previous version.
Release channels also become possible. A preview system can load a new version while the stable channel remains on the existing artifact. User groups, countries, or tenants can receive different versions without requiring a separate host build for each variant.
A real scenario might look like this:
Italy → Tasks Remote v2Germany → Tasks Remote v1Preview → Tasks Remote v2The Italian team has completed and deployed version 2. The new version is first verified in the preview channel and then activated for Italy. Germany remains on version 1 for the time being. As long as the mapping is managed outside the host artifact, no new host build is required.
Later binding therefore primarily makes it possible to point an already anticipated capability to a different version or address. It does not automatically make the host extensible with arbitrary new remotes. For that, the host would also need a generic contract for discovery, navigation, permissions, and composition.
This freedom is the actual value of dynamic binding. It allows remote versions to be activated later and with finer granularity.
It does not eliminate complexity. It moves it.
A manifest is a new runtime dependency. If it is unavailable, the host may be unable to resolve its remotes. If it contains an incorrect name, an invalid URL, or a version that no longer exists, the failure may become visible only in the browser.
Even a technically reachable combination may be functionally or technically incompatible. The host may expect a contract that an older remote version does not yet fulfill. A remote may rely on global state or services that exist only in a particular host version. Shared libraries may introduce incompatible assumptions. A formally valid mapping is not yet a working integration.
Every additional dimension of variability therefore expands the test matrix. If three host versions, four remote versions, and two release channels are genuinely combined freely, this does not simply create more autonomy. It first creates more possible system states.
Dynamic binding also requires better observability. Support and operations must be able to determine which remote version an affected user actually loaded. Logs and traces should expose not only the host, but also resolved remote addresses, version information, and failed loading attempts.
Cache behavior also becomes part of the architecture. An updated manifest is of little use if the browser, CDN, or service worker still serves an old mapping. Conversely, a temporary failure should not cause a working artifact to be unnecessarily evicted from the cache.
Security questions become more important as well. Who is allowed to change a mapping? Which origins are trusted? May a tenant load every registered version, or only explicitly approved artifacts? How do you prevent manipulated configuration from introducing arbitrary code into the host?
Later binding increases operational freedom. It also shifts failures and decisions into later stages of the system.
Dynamic Binding Does Not Eliminate Release Coordination
Section titled “Dynamic Binding Does Not Eliminate Release Coordination”A common claim is that hosts and remotes must never be released together. Otherwise, they are supposedly not “real” microfrontends.
This confuses the ability to change independently with an obligation to deliver every change independently.
A functional enhancement may affect several product areas at the same time. A new workflow may require changes to navigation, task management, and reporting. A regulated release may treat the entire product as one unit. A migration may deliberately be activated atomically so that old and new contract variants do not have to be supported in parallel for an extended period.
Support teams and domain stakeholders may also need a clearly defined product state. “Host 4.2 with some runtime-resolved combination of compatible remotes” is not necessarily more helpful in every environment than an explicitly approved composition.
A shared release is therefore not automatically an antipattern.
The relevant question is:
Is this coordination necessary for functional or operational reasons, or is it merely enforced by technical coupling?
If several applications are released together because a change is intended to be activated as one unit, that is a deliberate decision. If even a small local fix in reporting triggers a complete release train for every remote because contracts are unstable or hidden dependencies exist, actual independence is missing.
Dynamic binding does not automatically solve this problem. A manifest can point to a new version later. But if that version works only with a new host and two additional remotes, the coordination remains. It merely moves from the build into the maintenance of runtime configuration.
The Boundary Between Static and Dynamic Is Less Binary Than It Appears
Section titled “The Boundary Between Static and Dynamic Is Less Binary Than It Appears”Real systems rarely fit completely into two clean categories. Decisions are often static in one place and dynamic in another.
Static Address, Dynamic Content
Section titled “Static Address, Dynamic Content”A host can permanently know the same address:
https://tasks.example.com/remote-entry.jsThe currently approved version behind that address can still change. The responsible team publishes a new artifact, replaces the previous delivery, or redirects routing to another version.
From the host’s perspective, the binding remains static. Activation happens outside the host.
This variant may already provide enough independence. The host needs neither a manifest nor a registry, while the remote team can still publish new versions behind a stable address.
It does require the switch behind that address to be controlled. Rollback, cache invalidation, and traceability of the delivered version remain operational responsibilities.
Static Address with Dynamic Routing
Section titled “Static Address with Dynamic Routing”The stable address may also point to a gateway or CDN:
Host │ ▼https://remotes.example.com/tasks │ ├── Italy → v2 ├── Germany → v1 └── Preview → v2The host still knows only one fixed entry point. The infrastructure decides which version is actually delivered based on region, headers, cookies, tenant, or release channel.
The dynamic behavior therefore lies not in the frontend, but in the routing layer.
For the architectural decision, this does not make a fundamental difference: the remote mapping is changed after the host build. Technically, however, moving this responsibility can make sense when the gateway or CDN already provides established rules for releases, rollbacks, and regional delivery.
Dynamic Manifest, Organizationally Fixed Release
Section titled “Dynamic Manifest, Organizationally Fixed Release”The reverse case is also possible. The host loads its remote addresses from a manifest at runtime, but that manifest is changed only as part of a shared, controlled product release.
Technically, the binding is dynamic. Organizationally, it is operated in a largely static manner.
That is not necessarily a mistake. The manifest may be needed to use the same host artifact in several environments while production changes continue to be released together. The technical ability to activate independently does not oblige the organization to use it for every change.
Different Strategies in the Same Host
Section titled “Different Strategies in the Same Host”The decision does not have to be the same for every remote:
Host├── Core process statically bound├── Master data statically bound├── Reporting dynamically bound└── Country feature dynamic by regionThe core process and master data may change only together with the product and are intended to exist in a clearly reproducible combination. Reporting, by contrast, evolves at its own pace. A country-specific feature must be resolved based on regional approval.
A single global federation strategy would unnecessarily force these different requirements into one model. The appropriate binding point may vary by remote.
Dynamic Binding Is Not Organizational Autonomy
Section titled “Dynamic Binding Is Not Organizational Autonomy”Dynamic binding can support autonomous activation. It does not create autonomous teams.
A remote remains tightly coupled if its contracts are unstable, if it depends on a global store, or if it works only with a specific host version. If several remotes must be updated synchronously, a registry does not help. The fact that each address can be changed individually does not mean that each change can be owned independently.
Likewise, no genuine autonomy exists if every manifest change must be coordinated manually by a central platform team. The technical decision may have been deferred, but organizational decision-making has not been distributed.
Conversely, static binding may provide sufficient autonomy. If a remote’s address remains stable, the responsible team can deploy independently behind it. As long as the integration contract remains compatible and normal remote releases do not require a new host build, there is a meaningful degree of independent changeability.
The technical category must therefore not be confused with the organizational outcome.
What matters is not only when a remote mapping can be changed. It also matters who is allowed to change it, which contracts must be respected, and whether the team can take responsibility for the consequences of that change.
When Is Static Binding Enough?
Section titled “When Is Static Binding Enough?”Static binding is particularly plausible when the topology of the product is stable. If there are only a few hosts and long-lived product areas, their mapping does not need to become changeable at runtime merely because the technology allows it.
Stable remote addresses also favor early binding. If a team can deploy independently behind a fixed address without requiring host changes, an important part of the desired autonomy has already been achieved.
Static binding also fits products that are deliberately released as controlled overall states. There may be functional, regulatory, or operational reasons for this. Reproducibility is then not an annoying remnant of a monolith, but a product property.
It also makes sense when a small test matrix and an easily understood composition matter more than maximum activation freedom. An additional manifest is not worthwhile if every production environment always uses the same remotes at the same addresses anyway.
Typical signs that static binding is sufficient include:
- The remote topology rarely changes.
- Addresses remain stable over long periods.
- There are no regional or tenant-specific versions.
- Normal remote releases already do not require a host build.
- Shared releases are functionally intentional.
- A runtime manifest would not remove a concrete release bottleneck.
Static binding is not a primitive preliminary stage on the way to a later “real” microfrontend architecture. It may be the appropriate functional, technical, and economic solution.
When Is Dynamic Binding Justified?
Section titled “When Is Dynamic Binding Justified?”Dynamic binding is plausible when the remote mapping genuinely needs to change after the host build.
A clear example is a host artifact that should be deployed unchanged across several environments while the remote addresses differ. Instead of building a new host for each environment, the host receives its mapping when it starts.
Independent activations and rollbacks can also provide real value. A remote team publishes a new version that first appears in a preview channel and is later released to selected user groups. If problems occur, the mapping can be reverted without delivering a new host.
Other plausible requirements include parallel version states for countries or tenants, optional capabilities, and hosts that serve as platforms for changing product areas. A completely new remote can only be added without a host change, however, if the host has a generic discovery, routing, and integration contract for it. Dynamic addresses alone do not turn a host into a plugin platform.
Dynamic binding is particularly justified when at least one of the following bottlenecks genuinely exists:
- The same host artifact must serve different environments.
- Remote versions need to be activated and rolled back independently.
- Preview, canary, or stable channels are required.
- Countries, tenants, or user groups use different approved versions.
- Several versions of a remote must be available in parallel.
- The host is explicitly designed as a platform and can discover new remotes through a generic contract.
- Infrastructure routing should determine the concrete delivery.
The technical capability alone is not sufficient justification.
The organization must also be able to handle the additional runtime contracts, failure modes, and version combinations. This requires a robust activation process, clear compatibility rules, meaningful telemetry, and unambiguous responsibility for invalid mappings.
Anyone who demands independent activation must also enable independent rollback, diagnosis, and support.
The Economic Value Lies in the Bottleneck It Removes
Section titled “The Economic Value Lies in the Bottleneck It Removes”The decision has an economic dimension that is easily overlooked in technical discussions.
Static binding can reduce costs. It requires fewer platform components, can limit the number of mappings supported at the same time and therefore the test matrix, and makes troubleshooting easier. Product states can be reproduced more clearly. The additional monitoring needs of a dynamic resolution layer disappear, although remote loading failures and the artifacts actually delivered must still remain observable.
These savings are not spectacular. But they are continuous: less infrastructure, less operational logic, fewer combinations, and less knowledge that has to be maintained across several teams.
Dynamic binding can likewise create substantial economic value. Faster rollbacks reduce downtime. Regional releases enable staged market launches. Different release cadences prevent a productive area from having to wait for the slowest part of the overall system. Reusing the same host artifact across environments reduces build and approval effort.
But that value exists only when these capabilities are actually used.
If the mapping of a remote never changes after the host build, a runtime manifest is not an investment in autonomy. It is additional infrastructure that must be loaded, versioned, secured, monitored, and understood.
The economic question is therefore:
Which concrete bottleneck does later binding remove?
If there is no defensible answer, more dynamic behavior initially means only more system.
Technically, the Decision Remains Comparatively Small
Section titled “Technically, the Decision Remains Comparatively Small”The technical principle of static binding can be represented in simplified form like this:
const remotes = { tasks: 'https://tasks.example.com/remote-entry.js', members: 'https://members.example.com/remote-entry.js',};The mapping is part of the host configuration or the artifact created from it.
With dynamic binding, it is resolved later:
const remotes = await fetch('/remote-manifest.json').then((response) => response.json());Whether the loaded addresses are then integrated through Module Federation, Native Federation, import maps, Web Components, or a custom runtime mechanism does not change the strategic decision. Server-generated configuration, a registry, or a gateway can also perform the resolution.
The concrete implementation is interchangeable. The binding point and its consequences remain.
Teams using Nx should start with the current official Nx documentation for Module Federation. The specific generators, bundler integrations, and APIs depend on the version and framework. That is precisely why a custom configuration based on older examples is a poor starting point.
The Latest Useful Binding Point
Section titled “The Latest Useful Binding Point”Dynamic binding is not the more advanced form of microfrontends. It moves the decision about the remote mapping to a later point—and with it accepts responsibility for more runtime variants.
The right binding point is not the latest one technically possible, but the latest one for which a concrete functional or operational benefit exists.